Backed out 5 changesets (bug 1507950, bug 1503012, bug 1507943, bug 1512050, bug 1512008) for spidermonkey and jit failures
authorDorel Luca <dluca@mozilla.com>
Sat, 08 Dec 2018 00:11:12 +0200
changeset 508859 7eb42458e2d82b085a298cb3b7801cab2a3d4c51
parent 508858 2bdc76f2ab94b74ec344bff1c93a4adc711da77b
child 508867 21f77dc6811d390c937f927e5ce9b0320ea8c091
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)
bugs1507950, 1503012, 1507943, 1512050, 1512008
milestone65.0a1
backs out71253f35ac3cf2fa6f1c7b613443c2d494809c6a
f5c4eb4129904f4366800b968d9d5c1f245b620b
bc83a2fe5c178f5f555b5ff4566b387a028eccdc
345ad3e746e8e50d2365f6dac8ef5be6383d1afd
b2a0ae5e511537db7d316ce5a8de1b739290595b
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
Backed out 5 changesets (bug 1507950, bug 1503012, bug 1507943, bug 1512050, bug 1512008) for spidermonkey and jit failures Backed out changeset 71253f35ac3c (bug 1512008) Backed out changeset f5c4eb412990 (bug 1512050) Backed out changeset bc83a2fe5c17 (bug 1507950) Backed out changeset 345ad3e746e8 (bug 1507943) Backed out changeset b2a0ae5e5115 (bug 1503012)
build/gen_test_packages_manifest.py
js/src/builtin/Promise.cpp
js/src/builtin/Stream.cpp
js/src/builtin/Stream.h
js/src/jit-test/lib/w3c-testharness.js
js/src/jit-test/lib/web-platform-testharness.js
js/src/jit-test/tests/stream/bug-1512008.js
js/src/jit-test/tests/web-platform/streams/readable-streams/bad-strategies.js
js/src/jit-test/tests/web-platform/streams/readable-streams/bad-underlying-sources.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/constructor.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/garbage-collection.js
js/src/jit-test/tests/web-platform/streams/readable-streams/general.js
js/src/jit-test/tests/web-platform/streams/readable-streams/patched-global.js
js/src/jit-test/tests/web-platform/streams/readable-streams/reentrant-strategies.js
js/src/jit-test/tests/web-platform/streams/readable-streams/tee.js
js/src/jit-test/tests/web-platform/streams/readable-streams/templated.js
js/src/jsapi.cpp
js/src/jsapi.h
js/src/vm/Compartment-inl.h
testing/web-platform/meta/streams/readable-streams/bad-underlying-sources.dedicatedworker.html.ini
testing/web-platform/meta/streams/readable-streams/bad-underlying-sources.html.ini
testing/web-platform/meta/streams/readable-streams/bad-underlying-sources.serviceworker.https.html.ini
testing/web-platform/meta/streams/readable-streams/bad-underlying-sources.sharedworker.html.ini
testing/web-platform/meta/streams/readable-streams/constructor.dedicatedworker.html.ini
testing/web-platform/meta/streams/readable-streams/constructor.html.ini
testing/web-platform/meta/streams/readable-streams/constructor.serviceworker.https.html.ini
testing/web-platform/meta/streams/readable-streams/constructor.sharedworker.html.ini
testing/web-platform/meta/streams/readable-streams/garbage-collection.dedicatedworker.html.ini
testing/web-platform/meta/streams/readable-streams/garbage-collection.html.ini
testing/web-platform/meta/streams/readable-streams/garbage-collection.serviceworker.https.html.ini
testing/web-platform/meta/streams/readable-streams/garbage-collection.sharedworker.html.ini
testing/web-platform/meta/streams/readable-streams/general.dedicatedworker.html.ini
testing/web-platform/meta/streams/readable-streams/general.html.ini
testing/web-platform/meta/streams/readable-streams/general.serviceworker.https.html.ini
testing/web-platform/meta/streams/readable-streams/general.sharedworker.html.ini
--- a/build/gen_test_packages_manifest.py
+++ b/build/gen_test_packages_manifest.py
@@ -71,22 +71,19 @@ 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
 
 
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -3339,26 +3339,18 @@ static bool PromiseThenNewPromiseCapabil
   return true;
 }
 
 // ES2016, 25.4.5.3., steps 3-5.
 MOZ_MUST_USE bool js::OriginalPromiseThen(
     JSContext* cx, HandleObject promiseObj, HandleValue onFulfilled,
     HandleValue onRejected, MutableHandleObject dependent,
     CreateDependentPromise createDependent) {
-  RootedValue promiseVal(cx, ObjectValue(*promiseObj));
   Rooted<PromiseObject*> promise(
-      cx, UnwrapAndTypeCheckValue<PromiseObject>(cx, promiseVal, [=] {
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
-                                   JSMSG_INCOMPATIBLE_PROTO, "Promise", "then",
-                                   promiseObj->getClass()->name);
-      }));
-  if (!promise) {
-    return false;
-  }
+      cx, &CheckedUnwrap(promiseObj)->as<PromiseObject>());
 
   // Steps 3-4.
   Rooted<PromiseCapability> resultCapability(cx);
   if (!PromiseThenNewPromiseCapability(cx, promiseObj, createDependent,
                                        &resultCapability)) {
     return false;
   }
 
--- a/js/src/builtin/Stream.cpp
+++ b/js/src/builtin/Stream.cpp
@@ -4,17 +4,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "builtin/Stream.h"
 
 #include "js/Stream.h"
 
 #include "gc/Heap.h"
-#include "vm/Interpreter.h"
 #include "vm/JSContext.h"
 #include "vm/SelfHosting.h"
 
 #include "vm/Compartment-inl.h"
 #include "vm/List-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
@@ -117,16 +116,20 @@ inline static MOZ_MUST_USE T* TargetFrom
 inline static MOZ_MUST_USE bool ResetQueue(
     JSContext* cx, Handle<ReadableStreamController*> unwrappedContainer);
 
 inline static MOZ_MUST_USE bool InvokeOrNoop(JSContext* cx, HandleValue O,
                                              HandlePropertyName P,
                                              HandleValue arg,
                                              MutableHandleValue rval);
 
+static MOZ_MUST_USE JSObject* PromiseInvokeOrNoop(JSContext* cx, HandleValue O,
+                                                  HandlePropertyName P,
+                                                  HandleValue arg);
+
 static MOZ_MUST_USE JSObject* PromiseRejectedWithPendingError(JSContext* cx) {
   RootedValue exn(cx);
   if (!cx->isExceptionPending() || !GetAndClearException(cx, &exn)) {
     // Uncatchable error. This happens when a slow script is killed or a
     // worker is terminated. Propagate the uncatchable error. This will
     // typically kill off the calling asynchronous process: the caller
     // can't hook its continuation to the new rejected promise.
     return nullptr;
@@ -462,31 +465,19 @@ const Class TeeState::class_ = {"TeeStat
                              classOps, &cls::classSpec_};                   \
                                                                             \
   const Class cls::protoClass_ = {"object",                                 \
                                   JSCLASS_HAS_CACHED_PROTO(JSProto_##cls),  \
                                   JS_NULL_CLASS_OPS, &cls::classSpec_};
 
 /*** 3.2. Class ReadableStream **********************************************/
 
-/**
- * Characterizes the family of algorithms, (startAlgorithm, pullAlgorithm,
- * cancelAlgorithm), associated with a readable stream.
- *
- * See the comment on SetUpReadableStreamDefaultController().
- */
-enum class SourceAlgorithms {
-  Script,
-  Tee,
-};
-
 static MOZ_MUST_USE bool SetUpReadableStreamDefaultController(
-    JSContext* cx, Handle<ReadableStream*> stream, SourceAlgorithms algorithms,
-    HandleValue underlyingSource, HandleValue pullMethod,
-    HandleValue cancelMethod, double highWaterMark, HandleValue size);
+    JSContext* cx, Handle<ReadableStream*> stream, HandleValue underlyingSource,
+    double highWaterMarkVal, HandleValue size);
 
 static MOZ_MUST_USE ReadableByteStreamController*
 CreateExternalReadableByteStreamController(
     JSContext* cx, Handle<ReadableStream*> stream,
     JS::ReadableStreamUnderlyingSource* source);
 
 ReadableStream* ReadableStream::createExternalSourceStream(
     JSContext* cx, JS::ReadableStreamUnderlyingSource* source,
@@ -507,21 +498,16 @@ ReadableStream* ReadableStream::createEx
 }
 
 static MOZ_MUST_USE bool MakeSizeAlgorithmFromSizeFunction(JSContext* cx,
                                                            HandleValue size);
 
 static MOZ_MUST_USE bool ValidateAndNormalizeHighWaterMark(
     JSContext* cx, HandleValue highWaterMarkVal, double* highWaterMark);
 
-static MOZ_MUST_USE bool
-SetUpReadableStreamDefaultControllerFromUnderlyingSource(
-    JSContext* cx, Handle<ReadableStream*> stream, HandleValue underlyingSource,
-    double highWaterMark, HandleValue sizeAlgorithm);
-
 /**
  * Streams spec, 3.2.3. new ReadableStream(underlyingSource = {}, strategy = {})
  */
 bool ReadableStream::constructor(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   if (!ThrowIfNotConstructing(cx, args, "ReadableStream")) {
     return false;
@@ -614,18 +600,18 @@ bool ReadableStream::constructor(JSConte
                                              &highWaterMark)) {
         return false;
       }
     }
 
     // Step 7.d: Perform
     //           ? SetUpReadableStreamDefaultControllerFromUnderlyingSource(
     //           this, underlyingSource, highWaterMark, sizeAlgorithm).
-    if (!SetUpReadableStreamDefaultControllerFromUnderlyingSource(
-            cx, stream, underlyingSource, highWaterMark, size)) {
+    if (!SetUpReadableStreamDefaultController(cx, stream, underlyingSource,
+                                              highWaterMark, size)) {
       return false;
     }
 
     args.rval().setObject(*stream);
     return true;
   }
 
   // Step 8: Otherwise, throw a RangeError exception.
@@ -820,24 +806,21 @@ CLASS_SPEC(ReadableStream, 0, SlotCount,
 // Streams spec, 3.3.2. AcquireReadableStreamDefaultReader ( stream )
 // Always inlined. See CreateReadableStreamDefaultReader.
 
 /**
  * Streams spec, 3.3.3. CreateReadableStream (
  *                          startAlgorithm, pullAlgorithm, cancelAlgorithm
  *                          [, highWaterMark [, sizeAlgorithm ] ] )
  *
- * The start/pull/cancelAlgorithm arguments are represented instead as four
- * arguments: sourceAlgorithms, underlyingSource, pullMethod, cancelMethod.
- * See the comment on SetUpReadableStreamDefaultController.
+ * The start/pull/cancelAlgorithm arguments are represented as a single
+ * underlyingSource argument; see SetUpReadableStreamDefaultController().
  */
 MOZ_MUST_USE ReadableStream* CreateReadableStream(
-    JSContext* cx, SourceAlgorithms sourceAlgorithms,
-    HandleValue underlyingSource, HandleValue pullMethod = UndefinedHandleValue,
-    HandleValue cancelMethod = UndefinedHandleValue, double highWaterMark = 1,
+    JSContext* cx, HandleValue underlyingSource, double highWaterMark = 1,
     HandleValue sizeAlgorithm = UndefinedHandleValue,
     HandleObject proto = nullptr) {
   cx->check(underlyingSource, sizeAlgorithm, proto);
   MOZ_ASSERT(sizeAlgorithm.isUndefined() || IsCallable(sizeAlgorithm));
 
   // Step 1: If highWaterMark was not passed, set it to 1 (implicit).
   // Step 2: If sizeAlgorithm was not passed, set it to an algorithm that
   // returns 1 (implicit). Step 3: Assert: ! IsNonNegativeNumber(highWaterMark)
@@ -851,20 +834,18 @@ MOZ_MUST_USE ReadableStream* CreateReada
     return nullptr;
   }
 
   // Step 6: Let controller be ObjectCreate(the original value of
   //         ReadableStreamDefaultController's prototype property).
   // Step 7: Perform ? SetUpReadableStreamDefaultController(stream,
   //         controller, startAlgorithm, pullAlgorithm, cancelAlgorithm,
   //         highWaterMark, sizeAlgorithm).
-
-  if (!SetUpReadableStreamDefaultController(
-          cx, stream, sourceAlgorithms, underlyingSource, pullMethod,
-          cancelMethod, highWaterMark, sizeAlgorithm)) {
+  if (!SetUpReadableStreamDefaultController(cx, stream, underlyingSource,
+                                            highWaterMark, sizeAlgorithm)) {
     return nullptr;
   }
 
   // Step 8: Return stream.
   return stream;
 }
 
 // Streams spec, 3.3.4. CreateReadableByteStream (
@@ -1175,18 +1156,18 @@ static MOZ_MUST_USE JSObject* ReadableSt
   // Step 13/14.d: Return cancelPromise.
   RootedObject cancelPromise(cx, unwrappedTeeState->cancelPromise());
   if (!cx->compartment()->wrap(cx, &cancelPromise)) {
     return nullptr;
   }
   return cancelPromise;
 }
 
-static MOZ_MUST_USE bool ReadableStreamControllerError(
-    JSContext* cx, Handle<ReadableStreamController*> unwrappedController,
+static MOZ_MUST_USE bool ReadableStreamDefaultControllerErrorIfNeeded(
+    JSContext* cx, Handle<ReadableStreamDefaultController*> unwrappedController,
     HandleValue e);
 
 /**
  * Streams spec, 3.3.9. step 18:
  * Upon rejection of reader.[[closedPromise]] with reason r,
  */
 static bool TeeReaderClosedHandler(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
@@ -1195,29 +1176,27 @@ static bool TeeReaderClosedHandler(JSCon
 
   // Step a: If closedOrErrored is false, then:
   if (!teeState->closedOrErrored()) {
     // Step a.iii: Set closedOrErrored to true.
     // Reordered to ensure that internal errors in the other steps don't
     // leave the teeState in an undefined state.
     teeState->setClosedOrErrored();
 
-    // Step a.i: Perform
-    //           ! ReadableStreamDefaultControllerError(
-    //               branch1.[[readableStreamController]], r).
+    // Step a.i: Perform ! ReadableStreamDefaultControllerErrorIfNeeded(
+    //                          branch1.[[readableStreamController]], r).
     Rooted<ReadableStreamDefaultController*> branch1(cx, teeState->branch1());
-    if (!ReadableStreamControllerError(cx, branch1, reason)) {
+    if (!ReadableStreamDefaultControllerErrorIfNeeded(cx, branch1, reason)) {
       return false;
     }
 
-    // Step a.ii: Perform
-    //            ! ReadableStreamDefaultControllerError(
-    //                branch2.[[readableStreamController]], r).
+    // Step a.ii: Perform ! ReadableStreamDefaultControllerErrorIfNeeded(
+    //                          branch2.[[readableStreamController]], r).
     Rooted<ReadableStreamDefaultController*> branch2(cx, teeState->branch2());
-    if (!ReadableStreamControllerError(cx, branch2, reason)) {
+    if (!ReadableStreamDefaultControllerErrorIfNeeded(cx, branch2, reason)) {
       return false;
     }
   }
 
   args.rval().setUndefined();
   return true;
 }
 
@@ -1266,32 +1245,30 @@ static MOZ_MUST_USE bool ReadableStreamT
   // which one to perform based on class checks. For example, our
   // implementation of ReadableStreamControllerCallPullIfNeeded checks
   // whether the stream's underlyingSource is a TeeState object.
 
   // Step 16: Set branch1 to
   //          ! CreateReadableStream(startAlgorithm, pullAlgorithm,
   //                                 cancel1Algorithm).
   RootedValue underlyingSource(cx, ObjectValue(*teeState));
-  branch1Stream.set(
-      CreateReadableStream(cx, SourceAlgorithms::Tee, underlyingSource));
+  branch1Stream.set(CreateReadableStream(cx, underlyingSource));
   if (!branch1Stream) {
     return false;
   }
 
   Rooted<ReadableStreamDefaultController*> branch1(cx);
   branch1 = &branch1Stream->controller()->as<ReadableStreamDefaultController>();
   branch1->setTeeBranch1();
   teeState->setBranch1(branch1);
 
   // Step 17: Set branch2 to
   //          ! CreateReadableStream(startAlgorithm, pullAlgorithm,
   //                                 cancel2Algorithm).
-  branch2Stream.set(
-      CreateReadableStream(cx, SourceAlgorithms::Tee, underlyingSource));
+  branch2Stream.set(CreateReadableStream(cx, underlyingSource));
   if (!branch2Stream) {
     return false;
   }
 
   Rooted<ReadableStreamDefaultController*> branch2(cx);
   branch2 = &branch2Stream->controller()->as<ReadableStreamDefaultController>();
   branch2->setTeeBranch2();
   teeState->setBranch2(branch2);
@@ -2228,33 +2205,46 @@ static bool ControllerStartHandler(JSCon
   //          ! ReadableByteStreamControllerCallPullIfNeeded((controller).
   if (!ReadableStreamControllerCallPullIfNeeded(cx, controller)) {
     return false;
   }
   args.rval().setUndefined();
   return true;
 }
 
+static MOZ_MUST_USE bool ReadableStreamControllerError(
+    JSContext* cx, Handle<ReadableStreamController*> unwrappedController,
+    HandleValue e);
+
 /**
- * Streams spec, 3.9.11, step 12.a.
+ * Streams spec, 3.8.3, step 11.b.
  * and
- * Streams spec, 3.12.26, step 17.a.
+ * Streams spec, 3.10.3, step 16.b.
  */
 static bool ControllerStartFailedHandler(JSContext* cx, unsigned argc,
                                          Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
   Rooted<ReadableStreamController*> controller(
       cx, TargetFromHandler<ReadableStreamController>(args));
 
-  // 3.9.11, Step 12.a: Perform
-  //      ! ReadableStreamDefaultControllerError(controller, r).
-  // 3.12.26, Step 17.a: Perform
-  //      ! ReadableByteStreamControllerError(controller, r).
-  if (!ReadableStreamControllerError(cx, controller, args.get(0))) {
-    return false;
+  // 3.8.3, Step 11.b.i:
+  // Perform ! ReadableStreamDefaultControllerErrorIfNeeded(controller, r).
+  if (controller->is<ReadableStreamDefaultController>()) {
+    Rooted<ReadableStreamDefaultController*> defaultController(
+        cx, &controller->as<ReadableStreamDefaultController>());
+    return ReadableStreamDefaultControllerErrorIfNeeded(cx, defaultController,
+                                                        args.get(0));
+  }
+
+  // 3.10.3, Step 16.b.i: If stream.[[state]] is "readable", perform
+  //                      ! ReadableByteStreamControllerError(controller, r).
+  if (controller->stream()->readable()) {
+    if (!ReadableStreamControllerError(cx, controller, args.get(0))) {
+      return false;
+    }
   }
 
   args.rval().setUndefined();
   return true;
 }
 
 /**
  * Streams spec, 3.8.3.
@@ -2413,51 +2403,54 @@ static bool ReadableStreamDefaultControl
 
 /**
  * Streams spec, 3.8.4.4. error ( e )
  */
 static bool ReadableStreamDefaultController_error(JSContext* cx, unsigned argc,
                                                   Value* vp) {
   // Step 1: If ! IsReadableStreamDefaultController(this) is false, throw a
   //         TypeError exception.
+
   CallArgs args = CallArgsFromVp(argc, vp);
   Rooted<ReadableStreamDefaultController*> unwrappedController(
       cx, UnwrapAndTypeCheckThis<ReadableStreamDefaultController>(cx, args,
                                                                   "enqueue"));
   if (!unwrappedController) {
     return false;
   }
 
-  // Step 2: Perform ! ReadableStreamDefaultControllerError(this, e).
+  // Step 2: Let stream be this.[[controlledReadableStream]].
+  // Step 3: If stream.[[state]] is not "readable", throw a TypeError exception.
+  if (!unwrappedController->stream()->readable()) {
+    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+                              JSMSG_READABLESTREAMCONTROLLER_NOT_READABLE,
+                              "error");
+    return false;
+  }
+
+  // Step 4: Perform ! ReadableStreamDefaultControllerError(this, e).
   if (!ReadableStreamControllerError(cx, unwrappedController, args.get(0))) {
     return false;
   }
-
   args.rval().setUndefined();
   return true;
 }
 
 static const JSPropertySpec ReadableStreamDefaultController_properties[] = {
     JS_PSG("desiredSize", ReadableStreamDefaultController_desiredSize, 0),
     JS_PS_END};
 
 static const JSFunctionSpec ReadableStreamDefaultController_methods[] = {
     JS_FN("close", ReadableStreamDefaultController_close, 0, 0),
     JS_FN("enqueue", ReadableStreamDefaultController_enqueue, 1, 0),
     JS_FN("error", ReadableStreamDefaultController_error, 1, 0), JS_FS_END};
 
 CLASS_SPEC(ReadableStreamDefaultController, 0, SlotCount,
            ClassSpec::DontDefineConstructor, 0, JS_NULL_CLASS_OPS);
 
-static MOZ_MUST_USE JSObject* PromiseCall(JSContext* cx, HandleValue F,
-                                          HandleValue V, HandleValue arg);
-
-static void ReadableStreamControllerClearAlgorithms(
-    ReadableStreamController* controller);
-
 /**
  * Unified implementation of ReadableStream controllers' [[CancelSteps]]
  * internal methods.
  * Streams spec, 3.8.5.1. [[CancelSteps]] ( reason )
  * and
  * Streams spec, 3.10.5.1. [[CancelSteps]] ( reason )
  */
 static MOZ_MUST_USE JSObject* ReadableStreamControllerCancelSteps(
@@ -2489,94 +2482,66 @@ static MOZ_MUST_USE JSObject* ReadableSt
   RootedValue unwrappedUnderlyingSource(cx);
   unwrappedUnderlyingSource = unwrappedController->underlyingSource();
 
   // Step 1 of 3.8.5.1, step 2 of 3.10.5.1: Perform ! ResetQueue(this).
   if (!ResetQueue(cx, unwrappedController)) {
     return nullptr;
   }
 
-  // Step 2 of 3.8.5.1, step 3 of 3.10.5.1: Let result be the result of
-  //     performing this.[[cancelAlgorithm]], passing reason.
-  //
-  // Our representation of cancel algorithms is a bit awkward, for
-  // performance, so we must figure out which algorithm is being invoked.
-  RootedObject result(cx);
+  // Step 2 of 3.8.5.1, step 3 of 3.10.5.1:
+  // Return ! PromiseInvokeOrNoop(this.[[underlying(Byte)Source]],
+  //                              "cancel", « reason »)
+  // Note: this special-cases the underlying source of tee'd stream's
+  // branches. Instead of storing a JSFunction as the "cancel" property on
+  // those, we check if the source is a, maybe wrapped, TeeState instance
+  // and manually dispatch to the right internal function. TeeState is fully
+  // under our control, so this isn't content-observable.
   if (IsMaybeWrapped<TeeState>(unwrappedUnderlyingSource)) {
-    // The cancel algorithm given in ReadableStreamTee step 13 or 14.
-    MOZ_ASSERT(unwrappedUnderlyingSource.toObject().is<TeeState>(),
-               "tee streams and controllers are always same-compartment with "
-               "the TeeState object");
-    Rooted<TeeState*> unwrappedTeeState(
-        cx, &unwrappedUnderlyingSource.toObject().as<TeeState>());
-    Rooted<ReadableStreamDefaultController*> unwrappedDefaultController(
-        cx, &unwrappedController->as<ReadableStreamDefaultController>());
-    result = ReadableStreamTee_Cancel(cx, unwrappedTeeState,
-                                      unwrappedDefaultController, reason);
-  } else if (unwrappedController->hasExternalSource()) {
-    // An embedding-provided cancel algorithm.
+    Rooted<TeeState*> unwrappedteeState(cx);
+    unwrappedteeState =
+        &unwrappedUnderlyingSource.toObject().unwrapAs<TeeState>();
+    Rooted<ReadableStreamDefaultController*> unwrappedDefaultController(cx);
+    unwrappedDefaultController =
+        &unwrappedController->as<ReadableStreamDefaultController>();
+    return ReadableStreamTee_Cancel(cx, unwrappedteeState,
+                                    unwrappedDefaultController, reason);
+  }
+
+  if (unwrappedController->hasExternalSource()) {
     RootedValue rval(cx);
     {
       AutoRealm ar(cx, unwrappedController);
       JS::ReadableStreamUnderlyingSource* source =
           unwrappedController->externalSource();
       Rooted<ReadableStream*> stream(cx, unwrappedController->stream());
       RootedValue wrappedReason(cx, reason);
       if (!cx->compartment()->wrap(cx, &wrappedReason)) {
         return nullptr;
       }
 
       cx->check(stream, wrappedReason);
       rval = source->cancel(cx, stream, wrappedReason);
     }
 
-    // Make sure the ReadableStreamControllerClearAlgorithms call below is
-    // reached, even on error.
     if (!cx->compartment()->wrap(cx, &rval)) {
-      result = nullptr;
-    } else {
-      result = PromiseObject::unforgeableResolve(cx, rval);
+      return nullptr;
     }
-  } else {
-    // The algorithm created in
-    // SetUpReadableByteStreamControllerFromUnderlyingSource step 5.
-    RootedValue unwrappedCancelMethod(cx, unwrappedController->cancelMethod());
-    if (unwrappedCancelMethod.isUndefined()) {
-      // CreateAlgorithmFromUnderlyingMethod step 7.
-      result = PromiseObject::unforgeableResolve(cx, UndefinedHandleValue);
-    } else {
-      // CreateAlgorithmFromUnderlyingMethod steps 6.c.i-ii.
-      {
-        AutoRealm ar(cx, &unwrappedCancelMethod.toObject());
-        RootedValue underlyingSource(cx, unwrappedUnderlyingSource);
-        if (!cx->compartment()->wrap(cx, &underlyingSource)) {
-          return nullptr;
-        }
-        RootedValue wrappedReason(cx, reason);
-        if (!cx->compartment()->wrap(cx, &wrappedReason)) {
-          return nullptr;
-        }
-
-        // If PromiseCall fails, don't bail out until after the
-        // ReadableStreamControllerClearAlgorithms call below.
-        result = PromiseCall(cx, unwrappedCancelMethod, underlyingSource,
-                             wrappedReason);
-      }
-      if (!cx->compartment()->wrap(cx, &result)) {
-        result = nullptr;
-      }
-    }
-  }
-
-  // Step 3 of 3.8.5.1, step 4 of 3.10.5.1: Perform
-  //      ! ReadableByteStreamControllerClearAlgorithms(this).
-  ReadableStreamControllerClearAlgorithms(unwrappedController);
-
-  // Step 4 of 3.8.5.1, step 5 of 3.10.5.1: Return result.
-  return result;
+    return PromiseObject::unforgeableResolve(cx, rval);
+  }
+
+  // If the stream and its controller aren't in the cx compartment, we have
+  // to ensure that the underlying source is correctly wrapped before
+  // operating on it.
+  if (!cx->compartment()->wrap(cx, &unwrappedUnderlyingSource)) {
+    return nullptr;
+  }
+
+  return PromiseInvokeOrNoop(cx, unwrappedUnderlyingSource, cx->names().cancel,
+                             reason);
 }
 
 inline static MOZ_MUST_USE bool DequeueValue(
     JSContext* cx, Handle<ReadableStreamController*> unwrappedContainer,
     MutableHandleValue chunk);
 
 /**
  * Streams spec, 3.8.5.2. ReadableStreamDefaultController [[PullSteps]](
@@ -2702,20 +2667,22 @@ static bool ControllerPullFailedHandler(
   HandleValue e = args.get(0);
 
   Rooted<ReadableStreamController*> controller(
       cx, UnwrapCalleeSlot<ReadableStreamController>(cx, args, 0));
   if (!controller) {
     return false;
   }
 
-  // Step a: Perform ! ReadableStreamDefaultControllerError(controller, e).
-  //         (ReadableByteStreamControllerError in 3.12.3.)
-  if (!ReadableStreamControllerError(cx, controller, e)) {
-    return false;
+  // Step a: If controller.[[controlledReadableStream]].[[state]] is "readable",
+  //         perform ! ReadableByteStreamControllerError(controller, e).
+  if (controller->stream()->readable()) {
+    if (!ReadableStreamControllerError(cx, controller, e)) {
+      return false;
+    }
   }
 
   args.rval().setUndefined();
   return true;
 }
 
 static bool ReadableStreamControllerShouldCallPull(
     ReadableStreamController* unwrappedController);
@@ -2727,18 +2694,17 @@ static MOZ_MUST_USE double ReadableStrea
  * Streams spec, 3.9.2
  *      ReadableStreamDefaultControllerCallPullIfNeeded ( controller )
  * Streams spec, 3.12.3.
  *      ReadableByteStreamControllerCallPullIfNeeded ( controller )
  */
 inline static MOZ_MUST_USE bool ReadableStreamControllerCallPullIfNeeded(
     JSContext* cx, Handle<ReadableStreamController*> unwrappedController) {
   // Step 1: Let shouldPull be
-  //         ! ReadableStreamDefaultControllerShouldCallPull(controller).
-  // (ReadableByteStreamDefaultControllerShouldCallPull in 3.12.3.)
+  //         ! ReadableByteStreamControllerShouldCallPull(controller).
   bool shouldPull = ReadableStreamControllerShouldCallPull(unwrappedController);
 
   // Step 2: If shouldPull is false, return.
   if (!shouldPull) {
     return true;
   }
 
   // Step 3: If controller.[[pulling]] is true,
@@ -2751,80 +2717,53 @@ inline static MOZ_MUST_USE bool Readable
   }
 
   // Step 4: Assert: controller.[[pullAgain]] is false.
   MOZ_ASSERT(!unwrappedController->pullAgain());
 
   // Step 5: Set controller.[[pulling]] to true.
   unwrappedController->setPulling();
 
-  // We use this variable in step 7. For ease of error-handling, we wrap it
-  // early.
+  // Step 6: Let pullPromise be
+  //         ! PromiseInvokeOrNoop(controller.[[underlyingByteSource]],
+  //                               "pull", controller).
   RootedObject wrappedController(cx, unwrappedController);
   if (!cx->compartment()->wrap(cx, &wrappedController)) {
     return false;
   }
-
-  // Step 6: Let pullPromise be the result of performing
-  //         controller.[[pullAlgorithm]].
-  // Our representation of pull algorithms is a bit awkward, for performance,
-  // so we must figure out which algorithm is being invoked.
-  RootedObject pullPromise(cx);
+  RootedValue controllerVal(cx, ObjectValue(*wrappedController));
   RootedValue unwrappedUnderlyingSource(
       cx, unwrappedController->underlyingSource());
+  RootedObject pullPromise(cx);
 
   if (IsMaybeWrapped<TeeState>(unwrappedUnderlyingSource)) {
-    // The pull algorithm given in ReadableStreamTee step 12.
     MOZ_ASSERT(unwrappedUnderlyingSource.toObject().is<TeeState>(),
                "tee streams and controllers are always same-compartment with "
                "the TeeState object");
     Rooted<TeeState*> unwrappedTeeState(
         cx, &unwrappedUnderlyingSource.toObject().as<TeeState>());
     pullPromise = ReadableStreamTee_Pull(cx, unwrappedTeeState);
   } else if (unwrappedController->hasExternalSource()) {
-    // An embedding-provided pull algorithm.
     {
       AutoRealm ar(cx, unwrappedController);
       JS::ReadableStreamUnderlyingSource* source =
           unwrappedController->externalSource();
       Rooted<ReadableStream*> stream(cx, unwrappedController->stream());
       double desiredSize =
           ReadableStreamControllerGetDesiredSizeUnchecked(unwrappedController);
       source->requestData(cx, stream, desiredSize);
     }
     pullPromise = PromiseObject::unforgeableResolve(cx, UndefinedHandleValue);
   } else {
-    // The pull algorithm created in
-    // SetUpReadableStreamDefaultControllerFromUnderlyingSource step 4.
-    RootedValue unwrappedPullMethod(cx, unwrappedController->pullMethod());
-    if (unwrappedPullMethod.isUndefined()) {
-      // CreateAlgorithmFromUnderlyingMethod step 7.
-      pullPromise = PromiseObject::unforgeableResolve(cx, UndefinedHandleValue);
-    } else {
-      // CreateAlgorithmFromUnderlyingMethod step 6.b.i.
-      {
-        AutoRealm ar(cx, &unwrappedPullMethod.toObject());
-        RootedValue underlyingSource(cx, unwrappedUnderlyingSource);
-        if (!cx->compartment()->wrap(cx, &underlyingSource)) {
-          return false;
-        }
-        RootedValue controller(cx, ObjectValue(*unwrappedController));
-        if (!cx->compartment()->wrap(cx, &controller)) {
-          return false;
-        }
-        pullPromise =
-            PromiseCall(cx, unwrappedPullMethod, underlyingSource, controller);
-        if (!pullPromise) {
-          return false;
-        }
-      }
-      if (!cx->compartment()->wrap(cx, &pullPromise)) {
-        return false;
-      }
+    RootedValue underlyingSource(cx, unwrappedUnderlyingSource);
+    if (!cx->compartment()->wrap(cx, &underlyingSource)) {
+      return false;
     }
+    pullPromise = PromiseInvokeOrNoop(cx, underlyingSource, cx->names().pull,
+                                      controllerVal);
   }
   if (!pullPromise) {
     return false;
   }
 
   RootedObject onPullFulfilled(
       cx, NewHandler(cx, ControllerPullHandler, wrappedController));
   if (!onPullFulfilled) {
@@ -2887,38 +2826,16 @@ static bool ReadableStreamControllerShou
 
   // Step 7: If desiredSize > 0, return true.
   // Step 8: Return false.
   // Steps 7-8 of 3.12.24 are equivalent in our implementation.
   return desiredSize > 0;
 }
 
 /**
- * Streams spec, 3.9.4.
- *      ReadableStreamDefaultControllerClearAlgorithms ( controller )
- * and 3.12.4.
- *      ReadableByteStreamControllerClearAlgorithms ( controller )
- */
-static void ReadableStreamControllerClearAlgorithms(
-    ReadableStreamController* controller) {
-  // Step 1: Set controller.[[pullAlgorithm]] to undefined.
-  controller->setPullMethod(UndefinedHandleValue);
-
-  // Step 2: Set controller.[[cancelAlgorithm]] to undefined.
-  controller->setCancelMethod(UndefinedHandleValue);
-
-  // Step 3 (of 3.9.4 only) : Set controller.[[strategySizeAlgorithm]] to
-  // undefined.
-  if (controller->is<ReadableStreamDefaultController>()) {
-    controller->as<ReadableStreamDefaultController>().setStrategySize(
-        UndefinedHandleValue);
-  }
-}
-
-/**
  * Streams spec, 3.9.5. ReadableStreamDefaultControllerClose ( controller )
  */
 static MOZ_MUST_USE bool ReadableStreamDefaultControllerClose(
     JSContext* cx,
     Handle<ReadableStreamDefaultController*> unwrappedController) {
   // Step 1: Let stream be controller.[[controlledReadableStream]].
   Rooted<ReadableStream*> unwrappedStream(cx, unwrappedController->stream());
 
@@ -2955,69 +2872,71 @@ static MOZ_MUST_USE bool ReadableStreamD
   AssertSameCompartment(cx, chunk);
 
   // Step 1: Let stream be controller.[[controlledReadableStream]].
   Rooted<ReadableStream*> unwrappedStream(cx, unwrappedController->stream());
 
   // Step 2: Assert: controller.[[closeRequested]] is false.
   MOZ_ASSERT(!unwrappedController->closeRequested());
 
-  // Step 3: If ! IsReadableStreamLocked(stream) is true and
+  // Step 3: Assert: stream.[[state]] is "readable".
+  MOZ_ASSERT(unwrappedStream->readable());
+
+  // Step 4: If ! IsReadableStreamLocked(stream) is true and
   //         ! ReadableStreamGetNumReadRequests(stream) > 0, perform
   //         ! ReadableStreamFulfillReadRequest(stream, chunk, false).
   if (unwrappedStream->locked() &&
       ReadableStreamGetNumReadRequests(unwrappedStream) > 0) {
     if (!ReadableStreamFulfillReadOrReadIntoRequest(cx, unwrappedStream, chunk,
                                                     false)) {
       return false;
     }
   } else {
-    // Step 4: Otherwise,
-    // Step a: Let result be the result of performing
-    //         controller.[[strategySizeAlgorithm]], passing in chunk, and
-    //         interpreting the result as an ECMAScript completion value.
-    // Step c: (on success) Let chunkSize be result.[[Value]].
+    // Step 5: Otherwise,
+    // Step a: Let chunkSize be 1.
     RootedValue chunkSize(cx, NumberValue(1));
     bool success = true;
+
+    // Step b: If controller.[[strategySize]] is not undefined,
     RootedValue strategySize(cx, unwrappedController->strategySize());
     if (!strategySize.isUndefined()) {
+      // Step i: Set chunkSize to
+      //         Call(stream.[[strategySize]], undefined, chunk).
       if (!cx->compartment()->wrap(cx, &strategySize)) {
         return false;
       }
       success = Call(cx, strategySize, UndefinedHandleValue, chunk, &chunkSize);
     }
 
-    // Step d: Let enqueueResult be
+    // Step c: Let enqueueResult be
     //         EnqueueValueWithSize(controller, chunk, chunkSize).
     if (success) {
       success = EnqueueValueWithSize(cx, unwrappedController, chunk, chunkSize);
     }
 
     if (!success) {
-      // Step b: If result is an abrupt completion,
+      // Step b.ii: If chunkSize is an abrupt completion,
       // and
-      // Step e: If enqueueResult is an abrupt completion,
+      // Step d: If enqueueResult is an abrupt completion,
       RootedValue exn(cx);
       if (!cx->isExceptionPending() || !GetAndClearException(cx, &exn)) {
         // Uncatchable error. Die immediately without erroring the
         // stream.
         return false;
       }
 
-      // Step b.i: Perform ! ReadableStreamDefaultControllerError(
-      //           controller, result.[[Value]]).
-      // Step e.i: Perform ! ReadableStreamDefaultControllerError(
-      //           controller, enqueueResult.[[Value]]).
-      if (!ReadableStreamControllerError(cx, unwrappedController, exn)) {
+      // Step b.ii.1: Perform
+      //              ! ReadableStreamDefaultControllerErrorIfNeeded(
+      //                  controller, chunkSize.[[Value]]).
+      if (!ReadableStreamDefaultControllerErrorIfNeeded(cx, unwrappedController,
+                                                        exn)) {
         return false;
       }
 
-      // Step b.ii: Return result.
-      // Step e.ii: Return enqueueResult.
-      // (I.e., propagate the exception.)
+      // Step b.ii.2: Return chunkSize.
       cx->setPendingException(exn);
       return false;
     }
   }
 
   // Step 6: Perform
   //         ! ReadableStreamDefaultControllerCallPullIfNeeded(controller).
   // Step 7: Return.
@@ -3035,20 +2954,18 @@ static MOZ_MUST_USE bool ReadableStreamC
     JSContext* cx, Handle<ReadableStreamController*> unwrappedController,
     HandleValue e) {
   MOZ_ASSERT(!cx->isExceptionPending());
   AssertSameCompartment(cx, e);
 
   // Step 1: Let stream be controller.[[controlledReadableStream]].
   Rooted<ReadableStream*> unwrappedStream(cx, unwrappedController->stream());
 
-  // Step 2: If stream.[[state]] is not "readable", return.
-  if (!unwrappedStream->readable()) {
-    return true;
-  }
+  // Step 2: Assert: stream.[[state]] is "readable".
+  MOZ_ASSERT(unwrappedStream->readable());
 
   // Step 3 of 3.12.10:
   // Perform ! ReadableByteStreamControllerClearPendingPullIntos(controller).
   if (unwrappedController->is<ReadableByteStreamController>()) {
     Rooted<ReadableByteStreamController*> unwrappedByteStreamController(
         cx, &unwrappedController->as<ReadableByteStreamController>());
     if (!ReadableByteStreamControllerClearPendingPullIntos(
             cx, unwrappedByteStreamController)) {
@@ -3061,16 +2978,33 @@ static MOZ_MUST_USE bool ReadableStreamC
     return false;
   }
 
   // Step 4 (or 5): Perform ! ReadableStreamError(stream, e).
   return ReadableStreamErrorInternal(cx, unwrappedStream, e);
 }
 
 /**
+ * Streams spec, 3.9.7.
+ *      ReadableStreamDefaultControllerErrorIfNeeded ( controller, e ) nothrow
+ */
+static MOZ_MUST_USE bool ReadableStreamDefaultControllerErrorIfNeeded(
+    JSContext* cx, Handle<ReadableStreamDefaultController*> unwrappedController,
+    HandleValue e) {
+  MOZ_ASSERT(!cx->isExceptionPending());
+
+  // Step 1: If controller.[[controlledReadableStream]].[[state]] is "readable",
+  //         perform ! ReadableStreamDefaultControllerError(controller, e).
+  if (unwrappedController->stream()->readable()) {
+    return ReadableStreamControllerError(cx, unwrappedController, e);
+  }
+  return true;
+}
+
+/**
  * Streams spec, 3.9.8.
  *      ReadableStreamDefaultControllerGetDesiredSize ( controller )
  * Streams spec 3.12.14.
  *      ReadableByteStreamControllerGetDesiredSize ( controller )
  */
 static MOZ_MUST_USE double ReadableStreamControllerGetDesiredSizeUnchecked(
     ReadableStreamController* controller) {
   // Steps 1-4 done at callsites, so only assert that they have been done.
@@ -3087,54 +3021,28 @@ static MOZ_MUST_USE double ReadableStrea
  * Streams spec, 3.9.11.
  *      SetUpReadableStreamDefaultController(stream, controller,
  *          startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark,
  *          sizeAlgorithm )
  *
  * The standard algorithm takes a `controller` argument which must be a new,
  * blank object. This implementation creates a new controller instead.
  *
- * In the spec, three algorithms (startAlgorithm, pullAlgorithm,
- * cancelAlgorithm) are passed as arguments to this routine. This
- * implementation passes these "algorithms" as data, using four arguments:
- * sourceAlgorithms, underlyingSource, pullMethod, and cancelMethod. The
- * sourceAlgorithms argument tells how to interpret the other three:
- *
- * -   SourceAlgorithms::Script - We're creating a stream from a JS source.
- *     The caller is `new ReadableStream(underlyingSource)` or
- *     `JS::NewReadableDefaultStreamObject`. `underlyingSource` is the
- *     source; `pullMethod` and `cancelMethod` are its .pull and
- *     .cancel methods, which the caller has already extracted and
- *     type-checked: each one must be either a callable JS object or undefined.
- *
- *     Script streams use the start/pull/cancel algorithms defined in
- *     3.9.12. SetUpReadableStreamDefaultControllerFromUnderlyingSource, which
- *     call JS methods of the underlyingSource.
- *
- * -   SourceAlgorithms::Tee - We're creating a tee stream. `underlyingSource`
- *     is a TeeState object. `pullMethod` and `cancelMethod` are undefined.
- *
- *     Tee streams use the start/pull/cancel algorithms given in
- *     3.3.9. ReadableStreamTee.
+ * The standard algorithm takes startAlgorithm, pullAlgorithm, and
+ * cancelAlgorithm as separate arguments. We will do the same, but for now all
+ * of them are passed as a single underlyingSource argument--with a few
+ * user-visible differences in behavior (bug 1507943).
  *
  * Note: All arguments must be same-compartment with cx. ReadableStream
  * controllers are always created in the same compartment as the stream.
  */
 static MOZ_MUST_USE bool SetUpReadableStreamDefaultController(
-    JSContext* cx, Handle<ReadableStream*> stream,
-    SourceAlgorithms sourceAlgorithms, HandleValue underlyingSource,
-    HandleValue pullMethod, HandleValue cancelMethod, double highWaterMark,
-    HandleValue size) {
+    JSContext* cx, Handle<ReadableStream*> stream, HandleValue underlyingSource,
+    double highWaterMark, HandleValue size) {
   cx->check(stream, underlyingSource, size);
-  MOZ_ASSERT(pullMethod.isUndefined() || IsCallable(pullMethod));
-  MOZ_ASSERT(cancelMethod.isUndefined() || IsCallable(cancelMethod));
-  MOZ_ASSERT_IF(sourceAlgorithms != SourceAlgorithms::Script,
-                pullMethod.isUndefined());
-  MOZ_ASSERT_IF(sourceAlgorithms != SourceAlgorithms::Script,
-                cancelMethod.isUndefined());
   MOZ_ASSERT(highWaterMark >= 0);
   MOZ_ASSERT(size.isUndefined() || IsCallable(size));
 
   // Done elsewhere in the standard: Create the new controller.
   Rooted<ReadableStreamDefaultController*> controller(
       cx, NewBuiltinClassInstance<ReadableStreamDefaultController>(cx));
   if (!controller) {
     return false;
@@ -3157,30 +3065,33 @@ static MOZ_MUST_USE bool SetUpReadableSt
   controller->setFlags(0);
 
   // Step 5: Set controller.[[strategySizeAlgorithm]] to sizeAlgorithm
   //         and controller.[[strategyHWM]] to highWaterMark.
   controller->setStrategySize(size);
   controller->setStrategyHWM(highWaterMark);
 
   // Step 6: Set controller.[[pullAlgorithm]] to pullAlgorithm.
-  // (In this implementation, the pullAlgorithm is determined by the
-  // underlyingSource in combination with the pullMethod field.)
+  // Step 7: Set controller.[[cancelAlgorithm]] to cancelAlgorithm.
+  //
+  // For the moment, these algorithms are represented using the
+  // underlyingSource (bug 1507943). For example, when the underlying source
+  // is a TeeState, we use the ReadableStreamTee algorithms for pulling and
+  // canceling.
   controller->setUnderlyingSource(underlyingSource);
-  controller->setPullMethod(pullMethod);
-
-  // Step 7: Set controller.[[cancelAlgorithm]] to cancelAlgorithm.
-  controller->setCancelMethod(cancelMethod);
 
   // Step 8: Set stream.[[readableStreamController]] to controller.
   stream->setController(controller);
 
   // Step 9: Let startResult be the result of performing startAlgorithm.
+  // If this is a tee stream, the startAlgorithm does nothing and returns
+  // undefined.
   RootedValue startResult(cx);
-  if (sourceAlgorithms == SourceAlgorithms::Script) {
+  if (!underlyingSource.isObject() ||
+      !underlyingSource.toObject().is<TeeState>()) {
     RootedValue controllerVal(cx, ObjectValue(*controller));
     if (!InvokeOrNoop(cx, underlyingSource, cx->names().start, controllerVal,
                       &startResult)) {
       return false;
     }
   }
 
   // Step 10: Let startPromise be a promise resolved with startResult.
@@ -3207,71 +3118,16 @@ static MOZ_MUST_USE bool SetUpReadableSt
   if (!JS::AddPromiseReactions(cx, startPromise, onStartFulfilled,
                                onStartRejected)) {
     return false;
   }
 
   return true;
 }
 
-static MOZ_MUST_USE bool CreateAlgorithmFromUnderlyingMethod(
-    JSContext* cx, HandleValue underlyingObject,
-    const char* methodNameForErrorMessage, HandlePropertyName methodName,
-    MutableHandleValue method);
-
-/**
- * Streams spec, 3.9.12.
- *      SetUpReadableStreamDefaultControllerFromUnderlyingSource( stream,
- *          underlyingSource, highWaterMark, sizeAlgorithm )
- */
-static MOZ_MUST_USE bool
-SetUpReadableStreamDefaultControllerFromUnderlyingSource(
-    JSContext* cx, Handle<ReadableStream*> stream, HandleValue underlyingSource,
-    double highWaterMark, HandleValue sizeAlgorithm) {
-  // Step 1: Assert: underlyingSource is not undefined.
-  MOZ_ASSERT(!underlyingSource.isUndefined());
-
-  // Step 2: Let controller be ObjectCreate(the original value of
-  //         ReadableStreamDefaultController's prototype property).
-  // (Deferred to SetUpReadableStreamDefaultController.)
-
-  // Step 3: Let startAlgorithm be the following steps:
-  //         a. Return ? InvokeOrNoop(underlyingSource, "start",
-  //                                  « controller »).
-  SourceAlgorithms sourceAlgorithms = SourceAlgorithms::Script;
-
-  // Step 4: Let pullAlgorithm be
-  //         ? CreateAlgorithmFromUnderlyingMethod(underlyingSource, "pull",
-  //                                               0, « controller »).
-  RootedValue pullMethod(cx);
-  if (!CreateAlgorithmFromUnderlyingMethod(cx, underlyingSource,
-                                           "ReadableStream source.pull method",
-                                           cx->names().pull, &pullMethod)) {
-    return false;
-  }
-
-  // Step 5. Let cancelAlgorithm be
-  //         ? CreateAlgorithmFromUnderlyingMethod(underlyingSource,
-  //                                               "cancel", 1, « »).
-  RootedValue cancelMethod(cx);
-  if (!CreateAlgorithmFromUnderlyingMethod(
-          cx, underlyingSource, "ReadableStream source.cancel method",
-          cx->names().cancel, &cancelMethod)) {
-    return false;
-  }
-
-  // Step 6. Perform ? SetUpReadableStreamDefaultController(stream,
-  //             controller, startAlgorithm, pullAlgorithm, cancelAlgorithm,
-  //             highWaterMark, sizeAlgorithm).
-
-  return SetUpReadableStreamDefaultController(
-      cx, stream, sourceAlgorithms, underlyingSource, pullMethod, cancelMethod,
-      highWaterMark, sizeAlgorithm);
-}
-
 /*** 3.10. Class ReadableByteStreamController *******************************/
 
 #if 0  // disable user-defined byte streams
 
 /**
  * Streams spec, 3.10.3
  *      new ReadableByteStreamController ( stream, underlyingSource,
  *                                         highWaterMark )
@@ -4167,74 +4023,16 @@ inline static MOZ_MUST_USE bool AppendTo
   RootedValue val(cx, ObjectValue(*obj));
   if (!cx->compartment()->wrap(cx, &val)) {
     return false;
   }
   return list->append(cx, val);
 }
 
 /**
- * Streams spec, 6.3.1.
- *      CreateAlgorithmFromUnderlyingMethod ( underlyingObject, methodName,
- *                                            algoArgCount, extraArgs )
- *
- * This function only partly implements the standard algorithm. We do not
- * actually create a new JSFunction completely encapsulating the new algorithm.
- * Instead, this just gets the specified method and checks for errors. It's the
- * caller's responsibility to make sure that later, when the algorithm is
- * "performed", the appropriate steps are carried out.
- */
-static MOZ_MUST_USE bool CreateAlgorithmFromUnderlyingMethod(
-    JSContext* cx, HandleValue underlyingObject,
-    const char* methodNameForErrorMessage, HandlePropertyName methodName,
-    MutableHandleValue method) {
-  // Step 1: Assert: underlyingObject is not undefined.
-  MOZ_ASSERT(!underlyingObject.isUndefined());
-
-  // Step 2: Assert: ! IsPropertyKey(methodName) is true (implicit).
-  // Step 3: Assert: algoArgCount is 0 or 1 (omitted).
-  // Step 4: Assert: extraArgs is a List (omitted).
-
-  // Step 5: Let method be ? GetV(underlyingObject, methodName).
-  if (!GetProperty(cx, underlyingObject, methodName, method)) {
-    return false;
-  }
-
-  // Step 6: If method is not undefined,
-  if (!method.isUndefined()) {
-    // Step a: If ! IsCallable(method) is false, throw a TypeError
-    //         exception.
-    if (!IsCallable(method)) {
-      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                                JSMSG_NOT_FUNCTION, methodNameForErrorMessage);
-      return false;
-    }
-
-    // Step b: If algoArgCount is 0, return an algorithm that performs the
-    //         following steps:
-    //     Step i: Return ! PromiseCall(method, underlyingObject,
-    //             extraArgs).
-    // Step c: Otherwise, return an algorithm that performs the following
-    //         steps, taking an arg argument:
-    //     Step i: Let fullArgs be a List consisting of arg followed by the
-    //             elements of extraArgs in order.
-    //     Step ii: Return ! PromiseCall(method, underlyingObject,
-    //                                   fullArgs).
-    // (These steps are deferred to the code that performs the algorithm.
-    // See ReadableStreamControllerCancelSteps and
-    // ReadableStreamControllerCallPullIfNeeded.)
-    return true;
-  }
-
-  // Step 7: Return an algorithm which returns a promise resolved with
-  //         undefined (implicit).
-  return true;
-}
-
-/**
  * Streams spec, 6.3.2. InvokeOrNoop ( O, P, args )
  */
 inline static MOZ_MUST_USE bool InvokeOrNoop(JSContext* cx, HandleValue O,
                                              HandlePropertyName P,
                                              HandleValue arg,
                                              MutableHandleValue rval) {
   cx->check(O, P, arg);
 
@@ -4251,40 +4049,40 @@ inline static MOZ_MUST_USE bool InvokeOr
     return true;
   }
 
   // Step 5: Return ? Call(method, O, args).
   return Call(cx, method, O, arg, rval);
 }
 
 /**
- * Streams spec, 6.3.5. PromiseCall ( F, V, args )
- * As it happens, all callers pass exactly one argument.
+ * Streams spec, obsolete (previously 6.4.3) PromiseInvokeOrNoop ( O, P, args )
+ * Specialized to one arg, because that's what all stream related callers use.
  */
-static MOZ_MUST_USE JSObject* PromiseCall(JSContext* cx, HandleValue F,
-                                          HandleValue V, HandleValue arg) {
-  cx->check(F, V, arg);
-
-  // Step 1: Assert: ! IsCallable(F) is true.
-  MOZ_ASSERT(IsCallable(F));
-
-  // Step 2: Assert: V is not undefined.
-  MOZ_ASSERT(!V.isUndefined());
-
-  // Step 3: Assert: args is a List (implicit).
-  // Step 4: Let returnValue be Call(F, V, args).
-  RootedValue rval(cx);
-  if (!Call(cx, F, V, arg, &rval)) {
-    // Step 5: If returnValue is an abrupt completion, return a promise rejected
-    // with returnValue.[[Value]].
+static MOZ_MUST_USE JSObject* PromiseInvokeOrNoop(JSContext* cx, HandleValue O,
+                                                  HandlePropertyName P,
+                                                  HandleValue arg) {
+  cx->check(O, P, arg);
+
+  // Step 1: Assert: O is not undefined.
+  MOZ_ASSERT(!O.isUndefined());
+
+  // Step 2: Assert: ! IsPropertyKey(P) is true (implicit).
+  // Step 3: Assert: args is a List (omitted).
+
+  // Step 4: Let returnValue be InvokeOrNoop(O, P, args).
+  // Step 5: If returnValue is an abrupt completion, return a promise
+  //         rejected with returnValue.[[Value]].
+  RootedValue returnValue(cx);
+  if (!InvokeOrNoop(cx, O, P, arg, &returnValue)) {
     return PromiseRejectedWithPendingError(cx);
   }
 
   // Step 6: Otherwise, return a promise resolved with returnValue.[[Value]].
-  return PromiseObject::unforgeableResolve(cx, rval);
+  return PromiseObject::unforgeableResolve(cx, returnValue);
 }
 
 /**
  * Streams spec, 6.3.7. ValidateAndNormalizeHighWaterMark ( highWaterMark )
  */
 static MOZ_MUST_USE bool ValidateAndNormalizeHighWaterMark(
     JSContext* cx, HandleValue highWaterMarkVal, double* highWaterMark) {
   // Step 1: Set highWaterMark to ? ToNumber(highWaterMark).
@@ -4350,40 +4148,26 @@ JS_PUBLIC_API JSObject* JS::NewReadableD
     JS::HandleFunction size /* = nullptr */, double highWaterMark /* = 1 */,
     JS::HandleObject proto /* = nullptr */) {
   MOZ_ASSERT(!cx->zone()->isAtomsZone());
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
   cx->check(underlyingSource, size, proto);
   MOZ_ASSERT(highWaterMark >= 0);
 
-  // A copy of ReadableStream::constructor, with most of the
-  // argument-checking done implicitly by C++ type checking.
-  Rooted<ReadableStream*> stream(cx, ReadableStream::create(cx));
-  if (!stream) {
-    return nullptr;
-  }
-  RootedValue sourceVal(cx);
-  if (underlyingSource) {
-    sourceVal.setObject(*underlyingSource);
-  } else {
-    JSObject* source = NewBuiltinClassInstance<PlainObject>(cx);
+  RootedObject source(cx, underlyingSource);
+  if (!source) {
+    source = NewBuiltinClassInstance<PlainObject>(cx);
     if (!source) {
       return nullptr;
     }
-    sourceVal.setObject(*source);
-  }
+  }
+  RootedValue sourceVal(cx, ObjectValue(*source));
   RootedValue sizeVal(cx, size ? ObjectValue(*size) : UndefinedValue());
-
-  if (!SetUpReadableStreamDefaultControllerFromUnderlyingSource(
-          cx, stream, sourceVal, highWaterMark, sizeVal)) {
-    return nullptr;
-  }
-
-  return stream;
+  return CreateReadableStream(cx, sourceVal, highWaterMark, sizeVal);
 }
 
 JS_PUBLIC_API JSObject* JS::NewReadableExternalSourceStreamObject(
     JSContext* cx, JS::ReadableStreamUnderlyingSource* underlyingSource,
     HandleObject proto /* = nullptr */) {
   MOZ_ASSERT(!cx->zone()->isAtomsZone());
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
@@ -4782,16 +4566,25 @@ JS_PUBLIC_API bool JS::ReadableStreamErr
   cx->check(error);
 
   Rooted<ReadableStream*> unwrappedStream(
       cx, APIUnwrapAndDowncast<ReadableStream>(cx, streamObj));
   if (!unwrappedStream) {
     return false;
   }
 
+  // Step 3: If stream.[[state]] is not "readable", throw a TypeError exception.
+  if (!unwrappedStream->readable()) {
+    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+                              JSMSG_READABLESTREAMCONTROLLER_NOT_READABLE,
+                              "error");
+    return false;
+  }
+
+  // Step 4: Perform ! ReadableStreamDefaultControllerError(this, e).
   Rooted<ReadableStreamController*> unwrappedController(
       cx, unwrappedStream->controller());
   return ReadableStreamControllerError(cx, unwrappedController, error);
 }
 
 JS_PUBLIC_API bool JS::ReadableStreamReaderIsClosed(JSContext* cx,
                                                     HandleObject readerObj,
                                                     bool* result) {
--- a/js/src/builtin/Stream.h
+++ b/js/src/builtin/Stream.h
@@ -237,44 +237,29 @@ class StreamController : public NativeOb
 };
 
 class ReadableStreamController : public StreamController {
  public:
   /**
    * Memory layout for ReadableStream controllers, starting after the slots
    * reserved for queue container usage.
    *
-   * Storage of the internal slots listed in the standard is fairly
-   * straightforward except for [[pullAlgorithm]] and [[cancelAlgorithm]].
-   * These algorithms are not stored as JSFunction objects. Rather, there are
-   * three cases:
-   *
-   * -   Streams created with `new ReadableStream`: The methods are stored
-   *     in Slot_PullMethod and Slot_CancelMethod. The underlying source
-   *     object (`this` for these methods) is in Slot_UnderlyingSource.
+   * UnderlyingSource is usually treated as an opaque value. It might be a
+   * wrapped object from another compartment, but that case is handled
+   * correctly by all operations the controller might invoke on it.
    *
-   * -   External source streams. Slot_UnderlyingSource is a PrivateValue
-   *     pointing to the JS::ReadableStreamUnderlyingSource object. The
-   *     algorithms are implemented using the .pull() and .cancel() methods
-   *     of that object. Slot_Pull/CancelMethod are undefined.
-   *
-   * -   Tee streams. Slot_UnderlyingSource is a TeeState object. The
-   *     pull/cancel algorithms are implemented as separate functions in
-   *     Stream.cpp. Slot_Pull/CancelMethod are undefined.
-   *
-   * UnderlyingSource, PullMethod, and CancelMethod can be wrappers to objects
-   * in other compartments.
+   * The only case where we don't treat underlyingSource as an opaque value is
+   * if it's a TeeState. All functions operating on TeeState properly handle
+   * TeeState instances from other compartments.
    *
    * StrategyHWM and Flags are both primitive (numeric) values.
    */
   enum Slots {
     Slot_Stream = StreamController::SlotCount,
     Slot_UnderlyingSource,
-    Slot_PullMethod,
-    Slot_CancelMethod,
     Slot_StrategyHWM,
     Slot_Flags,
     SlotCount
   };
 
   enum ControllerFlags {
     Flag_Started = 1 << 0,
     Flag_Pulling = 1 << 1,
@@ -291,24 +276,16 @@ class ReadableStreamController : public 
   }
   void setStream(ReadableStream* stream) {
     setFixedSlot(Slot_Stream, ObjectValue(*stream));
   }
   Value underlyingSource() const { return getFixedSlot(Slot_UnderlyingSource); }
   void setUnderlyingSource(const Value& underlyingSource) {
     setFixedSlot(Slot_UnderlyingSource, underlyingSource);
   }
-  Value pullMethod() const { return getFixedSlot(Slot_PullMethod); }
-  void setPullMethod(const Value& pullMethod) {
-    setFixedSlot(Slot_PullMethod, pullMethod);
-  }
-  Value cancelMethod() const { return getFixedSlot(Slot_CancelMethod); }
-  void setCancelMethod(const Value& cancelMethod) {
-    setFixedSlot(Slot_CancelMethod, cancelMethod);
-  }
   JS::ReadableStreamUnderlyingSource* externalSource() const {
     static_assert(alignof(JS::ReadableStreamUnderlyingSource) >= 2,
                   "External underling sources are stored as PrivateValues, "
                   "so they must have even addresses");
     MOZ_ASSERT(hasExternalSource());
     return static_cast<JS::ReadableStreamUnderlyingSource*>(
         underlyingSource().toPrivate());
   }
deleted file mode 100644
--- a/js/src/jit-test/lib/w3c-testharness.js
+++ /dev/null
@@ -1,897 +0,0 @@
-/*global self*/
-/*jshint latedef: nofunc*/
-/*
-Distributed under both the W3C Test Suite License [1] and the W3C
-3-clause BSD License [2]. To contribute to a W3C Test Suite, see the
-policies and contribution forms [3].
-
-[1] http://www.w3.org/Consortium/Legal/2008/04-testsuite-license
-[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 ()
-{
-    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(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)
-    {
-        if (s.length > len) {
-            return s.substring(0, len - 3) + "...";
-        }
-        return s;
-    }
-
-    /*
-     * Return true if object is probably a Node object.
-     */
-    function is_node(object)
-    {
-        // I use duck-typing instead of instanceof, because
-        // instanceof doesn't work if the node is from another window (like an
-        // iframe's contentWindow):
-        // http://www.w3.org/Bugs/Public/show_bug.cgi?id=12295
-        if ("nodeType" in object &&
-            "nodeName" in object &&
-            "nodeValue" in object &&
-            "childNodes" in object) {
-            try {
-                object.nodeType;
-            } catch (e) {
-                // The object is probably Node.prototype or another prototype
-                // object that inherits from it, and not a Node instance.
-                return false;
-            }
-            return true;
-        }
-        return false;
-    }
-
-    /*
-     * Convert a value to a nice, human-readable string
-     */
-    function format_value(val, seen)
-    {
-        if (!seen) {
-            seen = [];
-        }
-        if (typeof val === "object" && val !== null) {
-            if (seen.includes(val)) {
-                return "[...]";
-            }
-            seen.push(val);
-        }
-        if (Array.isArray(val)) {
-            return "[" + val.map(function(x) {return format_value(x, seen);}).join(", ") + "]";
-        }
-
-        switch (typeof val) {
-        case "string":
-            val = val.replace("\\", "\\\\");
-            for (var i = 0; i < 32; i++) {
-                var replace = "\\";
-                switch (i) {
-                case 0: replace += "0"; break;
-                case 1: replace += "x01"; break;
-                case 2: replace += "x02"; break;
-                case 3: replace += "x03"; break;
-                case 4: replace += "x04"; break;
-                case 5: replace += "x05"; break;
-                case 6: replace += "x06"; break;
-                case 7: replace += "x07"; break;
-                case 8: replace += "b"; break;
-                case 9: replace += "t"; break;
-                case 10: replace += "n"; break;
-                case 11: replace += "v"; break;
-                case 12: replace += "f"; break;
-                case 13: replace += "r"; break;
-                case 14: replace += "x0e"; break;
-                case 15: replace += "x0f"; break;
-                case 16: replace += "x10"; break;
-                case 17: replace += "x11"; break;
-                case 18: replace += "x12"; break;
-                case 19: replace += "x13"; break;
-                case 20: replace += "x14"; break;
-                case 21: replace += "x15"; break;
-                case 22: replace += "x16"; break;
-                case 23: replace += "x17"; break;
-                case 24: replace += "x18"; break;
-                case 25: replace += "x19"; break;
-                case 26: replace += "x1a"; break;
-                case 27: replace += "x1b"; break;
-                case 28: replace += "x1c"; break;
-                case 29: replace += "x1d"; break;
-                case 30: replace += "x1e"; break;
-                case 31: replace += "x1f"; break;
-                }
-                val = val.replace(RegExp(String.fromCharCode(i), "g"), replace);
-            }
-            return '"' + val.replace(/"/g, '\\"') + '"';
-        case "boolean":
-        case "undefined":
-            return String(val);
-        case "number":
-            // In JavaScript, -0 === 0 and String(-0) == "0", so we have to
-            // special-case.
-            if (val === -0 && 1/val === -Infinity) {
-                return "-0";
-            }
-            return String(val);
-        case "object":
-            if (val === null) {
-                return "null";
-            }
-
-            // Special-case Node objects, since those come up a lot in my tests.  I
-            // ignore namespaces.
-            if (is_node(val)) {
-                switch (val.nodeType) {
-                case Node.ELEMENT_NODE:
-                    var ret = "<" + val.localName;
-                    for (var i = 0; i < val.attributes.length; i++) {
-                        ret += " " + val.attributes[i].name + '="' + val.attributes[i].value + '"';
-                    }
-                    ret += ">" + val.innerHTML + "</" + val.localName + ">";
-                    return "Element node " + truncate(ret, 60);
-                case Node.TEXT_NODE:
-                    return 'Text node "' + truncate(val.data, 60) + '"';
-                case Node.PROCESSING_INSTRUCTION_NODE:
-                    return "ProcessingInstruction node with target " + format_value(truncate(val.target, 60)) + " and data " + format_value(truncate(val.data, 60));
-                case Node.COMMENT_NODE:
-                    return "Comment node <!--" + truncate(val.data, 60) + "-->";
-                case Node.DOCUMENT_NODE:
-                    return "Document node with " + val.childNodes.length + (val.childNodes.length == 1 ? " child" : " children");
-                case Node.DOCUMENT_TYPE_NODE:
-                    return "DocumentType node";
-                case Node.DOCUMENT_FRAGMENT_NODE:
-                    return "DocumentFragment node with " + val.childNodes.length + (val.childNodes.length == 1 ? " child" : " children");
-                default:
-                    return "Node object of unknown type";
-                }
-            }
-
-        /* falls through */
-        default:
-            return typeof val + ' "' + truncate(String(val), 60) + '"';
-        }
-    }
-    expose(format_value, "format_value");
-
-    /*
-     * Assertions
-     */
-
-    function assert_true(actual, description)
-    {
-        assert(actual === true, "assert_true", description,
-                                "expected true got ${actual}", {actual:actual});
-    }
-    expose(assert_true, "assert_true");
-
-    function assert_false(actual, description)
-    {
-        assert(actual === false, "assert_false", description,
-                                 "expected false got ${actual}", {actual:actual});
-    }
-    expose(assert_false, "assert_false");
-
-    function same_value(x, y) {
-        if (y !== y) {
-            //NaN case
-            return x !== x;
-        }
-        if (x === 0 && y === 0) {
-            //Distinguish +0 and -0
-            return 1/x === 1/y;
-        }
-        return x === y;
-    }
-
-    function assert_equals(actual, expected, description)
-    {
-         /*
-          * Test if two primitives are equal or two objects
-          * are the same object
-          */
-        if (typeof actual != typeof expected) {
-            assert(false, "assert_equals", description,
-                          "expected (" + typeof expected + ") ${expected} but got (" + typeof actual + ") ${actual}",
-                          {expected:expected, actual:actual});
-            return;
-        }
-        assert(same_value(actual, expected), "assert_equals", description,
-                                             "expected ${expected} but got ${actual}",
-                                             {expected:expected, actual:actual});
-    }
-    expose(assert_equals, "assert_equals");
-
-    function assert_not_equals(actual, expected, description)
-    {
-         /*
-          * Test if two primitives are unequal or two objects
-          * are different objects
-          */
-        assert(!same_value(actual, expected), "assert_not_equals", description,
-                                              "got disallowed value ${actual}",
-                                              {actual:actual});
-    }
-    expose(assert_not_equals, "assert_not_equals");
-
-    function assert_in_array(actual, expected, description)
-    {
-        assert(expected.includes(actual), "assert_in_array", description,
-                                               "value ${actual} not in array ${expected}",
-                                               {actual:actual, expected:expected});
-    }
-    expose(assert_in_array, "assert_in_array");
-
-    function assert_object_equals(actual, expected, description)
-    {
-         //This needs to be improved a great deal
-         function check_equal(actual, expected, stack)
-         {
-             stack.push(actual);
-
-             var p;
-             for (p in actual) {
-                 assert(expected.hasOwnProperty(p), "assert_object_equals", description,
-                                                    "unexpected property ${p}", {p:p});
-
-                 if (typeof actual[p] === "object" && actual[p] !== null) {
-                     if (!stack.includes(actual[p])) {
-                         check_equal(actual[p], expected[p], stack);
-                     }
-                 } else {
-                     assert(same_value(actual[p], expected[p]), "assert_object_equals", description,
-                                                       "property ${p} expected ${expected} got ${actual}",
-                                                       {p:p, expected:expected, actual:actual});
-                 }
-             }
-             for (p in expected) {
-                 assert(actual.hasOwnProperty(p),
-                        "assert_object_equals", description,
-                        "expected property ${p} missing", {p:p});
-             }
-             stack.pop();
-         }
-         check_equal(actual, expected, []);
-    }
-    expose(assert_object_equals, "assert_object_equals");
-
-    function assert_array_equals(actual, expected, description)
-    {
-        assert(actual.length === expected.length,
-               "assert_array_equals", description,
-               "lengths differ, expected ${expected} got ${actual}",
-               {expected:expected.length, actual:actual.length});
-
-        for (var i = 0; i < actual.length; i++) {
-            assert(actual.hasOwnProperty(i) === expected.hasOwnProperty(i),
-                   "assert_array_equals", description,
-                   "property ${i}, property expected to be ${expected} but was ${actual}",
-                   {i:i, expected:expected.hasOwnProperty(i) ? "present" : "missing",
-                   actual:actual.hasOwnProperty(i) ? "present" : "missing"});
-            assert(same_value(expected[i], actual[i]),
-                   "assert_array_equals", description,
-                   "property ${i}, expected ${expected} but got ${actual}",
-                   {i:i, expected:expected[i], actual:actual[i]});
-        }
-    }
-    expose(assert_array_equals, "assert_array_equals");
-
-    function assert_approx_equals(actual, expected, epsilon, description)
-    {
-        /*
-         * Test if two primitive numbers are equal withing +/- epsilon
-         */
-        assert(typeof actual === "number",
-               "assert_approx_equals", description,
-               "expected a number but got a ${type_actual}",
-               {type_actual:typeof actual});
-
-        assert(Math.abs(actual - expected) <= epsilon,
-               "assert_approx_equals", description,
-               "expected ${expected} +/- ${epsilon} but got ${actual}",
-               {expected:expected, actual:actual, epsilon:epsilon});
-    }
-    expose(assert_approx_equals, "assert_approx_equals");
-
-    function assert_less_than(actual, expected, description)
-    {
-        /*
-         * Test if a primitive number is less than another
-         */
-        assert(typeof actual === "number",
-               "assert_less_than", description,
-               "expected a number but got a ${type_actual}",
-               {type_actual:typeof actual});
-
-        assert(actual < expected,
-               "assert_less_than", description,
-               "expected a number less than ${expected} but got ${actual}",
-               {expected:expected, actual:actual});
-    }
-    expose(assert_less_than, "assert_less_than");
-
-    function assert_greater_than(actual, expected, description)
-    {
-        /*
-         * Test if a primitive number is greater than another
-         */
-        assert(typeof actual === "number",
-               "assert_greater_than", description,
-               "expected a number but got a ${type_actual}",
-               {type_actual:typeof actual});
-
-        assert(actual > expected,
-               "assert_greater_than", description,
-               "expected a number greater than ${expected} but got ${actual}",
-               {expected:expected, actual:actual});
-    }
-    expose(assert_greater_than, "assert_greater_than");
-
-    function assert_between_exclusive(actual, lower, upper, description)
-    {
-        /*
-         * Test if a primitive number is between two others
-         */
-        assert(typeof actual === "number",
-               "assert_between_exclusive", description,
-               "expected a number but got a ${type_actual}",
-               {type_actual:typeof actual});
-
-        assert(actual > lower && actual < upper,
-               "assert_between_exclusive", description,
-               "expected a number greater than ${lower} " +
-               "and less than ${upper} but got ${actual}",
-               {lower:lower, upper:upper, actual:actual});
-    }
-    expose(assert_between_exclusive, "assert_between_exclusive");
-
-    function assert_less_than_equal(actual, expected, description)
-    {
-        /*
-         * Test if a primitive number is less than or equal to another
-         */
-        assert(typeof actual === "number",
-               "assert_less_than_equal", description,
-               "expected a number but got a ${type_actual}",
-               {type_actual:typeof actual});
-
-        assert(actual <= expected,
-               "assert_less_than_equal", description,
-               "expected a number less than or equal to ${expected} but got ${actual}",
-               {expected:expected, actual:actual});
-    }
-    expose(assert_less_than_equal, "assert_less_than_equal");
-
-    function assert_greater_than_equal(actual, expected, description)
-    {
-        /*
-         * Test if a primitive number is greater than or equal to another
-         */
-        assert(typeof actual === "number",
-               "assert_greater_than_equal", description,
-               "expected a number but got a ${type_actual}",
-               {type_actual:typeof actual});
-
-        assert(actual >= expected,
-               "assert_greater_than_equal", description,
-               "expected a number greater than or equal to ${expected} but got ${actual}",
-               {expected:expected, actual:actual});
-    }
-    expose(assert_greater_than_equal, "assert_greater_than_equal");
-
-    function assert_between_inclusive(actual, lower, upper, description)
-    {
-        /*
-         * Test if a primitive number is between to two others or equal to either of them
-         */
-        assert(typeof actual === "number",
-               "assert_between_inclusive", description,
-               "expected a number but got a ${type_actual}",
-               {type_actual:typeof actual});
-
-        assert(actual >= lower && actual <= upper,
-               "assert_between_inclusive", description,
-               "expected a number greater than or equal to ${lower} " +
-               "and less than or equal to ${upper} but got ${actual}",
-               {lower:lower, upper:upper, actual:actual});
-    }
-    expose(assert_between_inclusive, "assert_between_inclusive");
-
-    function assert_regexp_match(actual, expected, description) {
-        /*
-         * Test if a string (actual) matches a regexp (expected)
-         */
-        assert(expected.test(actual),
-               "assert_regexp_match", description,
-               "expected ${expected} but got ${actual}",
-               {expected:expected, actual:actual});
-    }
-    expose(assert_regexp_match, "assert_regexp_match");
-
-    function assert_class_string(object, class_string, description) {
-        assert_equals({}.toString.call(object), "[object " + class_string + "]",
-                      description);
-    }
-    expose(assert_class_string, "assert_class_string");
-
-
-    function _assert_own_property(name) {
-        return function(object, property_name, description)
-        {
-            assert(object.hasOwnProperty(property_name),
-                   name, description,
-                   "expected property ${p} missing", {p:property_name});
-        };
-    }
-    expose(_assert_own_property("assert_exists"), "assert_exists");
-    expose(_assert_own_property("assert_own_property"), "assert_own_property");
-
-    function assert_not_exists(object, property_name, description)
-    {
-        assert(!object.hasOwnProperty(property_name),
-               "assert_not_exists", description,
-               "unexpected property ${p} found", {p:property_name});
-    }
-    expose(assert_not_exists, "assert_not_exists");
-
-    function _assert_inherits(name) {
-        return function (object, property_name, description)
-        {
-            assert(typeof object === "object",
-                   name, description,
-                   "provided value is not an object");
-
-            assert("hasOwnProperty" in object,
-                   name, description,
-                   "provided value is an object but has no hasOwnProperty method");
-
-            assert(!object.hasOwnProperty(property_name),
-                   name, description,
-                   "property ${p} found on object expected in prototype chain",
-                   {p:property_name});
-
-            assert(property_name in object,
-                   name, description,
-                   "property ${p} not found in prototype chain",
-                   {p:property_name});
-        };
-    }
-    expose(_assert_inherits("assert_inherits"), "assert_inherits");
-    expose(_assert_inherits("assert_idl_attribute"), "assert_idl_attribute");
-
-    function assert_readonly(object, property_name, description)
-    {
-         var initial_value = object[property_name];
-         try {
-             //Note that this can have side effects in the case where
-             //the property has PutForwards
-             object[property_name] = initial_value + "a"; //XXX use some other value here?
-             assert(same_value(object[property_name], initial_value),
-                    "assert_readonly", description,
-                    "changing property ${p} succeeded",
-                    {p:property_name});
-         } finally {
-             object[property_name] = initial_value;
-         }
-    }
-    expose(assert_readonly, "assert_readonly");
-
-    function assert_throws(code, func, description)
-    {
-        try {
-            func.call(this);
-            assert(false, "assert_throws", description,
-                   "${func} did not throw", {func:func});
-        } catch (e) {
-            if (e instanceof AssertionError) {
-                throw e;
-            }
-            if (code === null) {
-                return;
-            }
-            if (typeof code === "object") {
-                assert(typeof e == "object" && "name" in e && e.name == code.name,
-                       "assert_throws", description,
-                       "${func} threw ${actual} (${actual_name}) expected ${expected} (${expected_name})",
-                                    {func:func, actual:e, actual_name:e.name,
-                                     expected:code,
-                                     expected_name:code.name});
-                return;
-            }
-
-            var code_name_map = {
-                INDEX_SIZE_ERR: 'IndexSizeError',
-                HIERARCHY_REQUEST_ERR: 'HierarchyRequestError',
-                WRONG_DOCUMENT_ERR: 'WrongDocumentError',
-                INVALID_CHARACTER_ERR: 'InvalidCharacterError',
-                NO_MODIFICATION_ALLOWED_ERR: 'NoModificationAllowedError',
-                NOT_FOUND_ERR: 'NotFoundError',
-                NOT_SUPPORTED_ERR: 'NotSupportedError',
-                INVALID_STATE_ERR: 'InvalidStateError',
-                SYNTAX_ERR: 'SyntaxError',
-                INVALID_MODIFICATION_ERR: 'InvalidModificationError',
-                NAMESPACE_ERR: 'NamespaceError',
-                INVALID_ACCESS_ERR: 'InvalidAccessError',
-                TYPE_MISMATCH_ERR: 'TypeMismatchError',
-                SECURITY_ERR: 'SecurityError',
-                NETWORK_ERR: 'NetworkError',
-                ABORT_ERR: 'AbortError',
-                URL_MISMATCH_ERR: 'URLMismatchError',
-                QUOTA_EXCEEDED_ERR: 'QuotaExceededError',
-                TIMEOUT_ERR: 'TimeoutError',
-                INVALID_NODE_TYPE_ERR: 'InvalidNodeTypeError',
-                DATA_CLONE_ERR: 'DataCloneError'
-            };
-
-            var name = code in code_name_map ? code_name_map[code] : code;
-
-            var name_code_map = {
-                IndexSizeError: 1,
-                HierarchyRequestError: 3,
-                WrongDocumentError: 4,
-                InvalidCharacterError: 5,
-                NoModificationAllowedError: 7,
-                NotFoundError: 8,
-                NotSupportedError: 9,
-                InvalidStateError: 11,
-                SyntaxError: 12,
-                InvalidModificationError: 13,
-                NamespaceError: 14,
-                InvalidAccessError: 15,
-                TypeMismatchError: 17,
-                SecurityError: 18,
-                NetworkError: 19,
-                AbortError: 20,
-                URLMismatchError: 21,
-                QuotaExceededError: 22,
-                TimeoutError: 23,
-                InvalidNodeTypeError: 24,
-                DataCloneError: 25,
-
-                EncodingError: 0,
-                NotReadableError: 0,
-                UnknownError: 0,
-                ConstraintError: 0,
-                DataError: 0,
-                TransactionInactiveError: 0,
-                ReadOnlyError: 0,
-                VersionError: 0,
-                OperationError: 0,
-            };
-
-            if (!(name in name_code_map)) {
-                throw new AssertionError('Test bug: unrecognized DOMException code "' + code + '" passed to assert_throws()');
-            }
-
-            var required_props = { code: name_code_map[name] };
-
-            if (required_props.code === 0 ||
-               (typeof e == "object" &&
-                "name" in e &&
-                e.name !== e.name.toUpperCase() &&
-                e.name !== "DOMException")) {
-                // New style exception: also test the name property.
-                required_props.name = name;
-            }
-
-            //We'd like to test that e instanceof the appropriate interface,
-            //but we can't, because we don't know what window it was created
-            //in.  It might be an instanceof the appropriate interface on some
-            //unknown other window.  TODO: Work around this somehow?
-
-            assert(typeof e == "object",
-                   "assert_throws", description,
-                   "${func} threw ${e} with type ${type}, not an object",
-                   {func:func, e:e, type:typeof e});
-
-            for (var prop in required_props) {
-                assert(typeof e == "object" && prop in e && e[prop] == required_props[prop],
-                       "assert_throws", description,
-                       "${func} threw ${e} that is not a DOMException " + code + ": property ${prop} is equal to ${actual}, expected ${expected}",
-                       {func:func, e:e, prop:prop, actual:e[prop], expected:required_props[prop]});
-            }
-        }
-    }
-    expose(assert_throws, "assert_throws");
-
-    function assert_unreached(description) {
-         assert(false, "assert_unreached", description,
-                "Reached unreachable code");
-    }
-    expose(assert_unreached, "assert_unreached");
-
-    function assert_any(assert_func, actual, expected_array)
-    {
-        var args = [].slice.call(arguments, 3);
-        var errors = [];
-        var passed = false;
-        forEach(expected_array,
-                function(expected)
-                {
-                    try {
-                        assert_func.apply(this, [actual, expected].concat(args));
-                        passed = true;
-                    } catch (e) {
-                        errors.push(e.message);
-                    }
-                });
-        if (!passed) {
-            throw new AssertionError(errors.join("\n\n"));
-        }
-    }
-    expose(assert_any, "assert_any");
-
-    /*
-     * 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
-     * functions (see below)
-     *
-     * A text node is represented as
-     *
-     * ["{text}", value]
-     *
-     * String values have a simple substitution syntax; ${foo} represents a variable foo.
-     *
-     * It is possible to embed logic in templates by using a function in a place where a
-     * node would usually go. The function must either return part of a template or null.
-     *
-     * In cases where a set of nodes are required as output rather than a single node
-     * with children it is possible to just use a list
-     * [node1, node2, node3]
-     *
-     * Usage:
-     *
-     * render(template, substitutions) - take a template and an object mapping
-     * variable names to parameters and return either a DOM node or a list of DOM nodes
-     *
-     * substitute(template, substitutions) - take a template and variable mapping object,
-     * make the variable substitutions and return the substituted template
-     *
-     */
-
-    function is_single_node(template)
-    {
-        return typeof template[0] === "string";
-    }
-
-    function substitute(template, substitutions)
-    {
-        if (typeof template === "function") {
-            var replacement = template(substitutions);
-            if (!replacement) {
-                return null;
-            }
-
-            return substitute(replacement, substitutions);
-        }
-
-        if (is_single_node(template)) {
-            return substitute_single(template, substitutions);
-        }
-
-        return filter(map(template, function(x) {
-                              return substitute(x, substitutions);
-                          }), function(x) {return x !== null;});
-    }
-
-    function substitute_single(template, substitutions)
-    {
-        var substitution_re = /\$\{([^ }]*)\}/g;
-
-        function do_substitution(input) {
-            var components = input.split(substitution_re);
-            var rv = [];
-            for (var i = 0; i < components.length; i += 2) {
-                rv.push(components[i]);
-                if (components[i + 1]) {
-                    rv.push(String(substitutions[components[i + 1]]));
-                }
-            }
-            return rv;
-        }
-
-        function substitute_attrs(attrs, rv)
-        {
-            rv[1] = {};
-            for (var name in template[1]) {
-                if (attrs.hasOwnProperty(name)) {
-                    var new_name = do_substitution(name).join("");
-                    var new_value = do_substitution(attrs[name]).join("");
-                    rv[1][new_name] = new_value;
-                }
-            }
-        }
-
-        function substitute_children(children, rv)
-        {
-            for (var i = 0; i < children.length; i++) {
-                if (children[i] instanceof Object) {
-                    var replacement = substitute(children[i], substitutions);
-                    if (replacement !== null) {
-                        if (is_single_node(replacement)) {
-                            rv.push(replacement);
-                        } else {
-                            extend(rv, replacement);
-                        }
-                    }
-                } else {
-                    extend(rv, do_substitution(String(children[i])));
-                }
-            }
-            return rv;
-        }
-
-        var rv = [];
-        rv.push(do_substitution(String(template[0])).join(""));
-
-        if (template[0] === "{text}") {
-            substitute_children(template.slice(1), rv);
-        } else {
-            substitute_attrs(template[1], rv);
-            substitute_children(template.slice(2), rv);
-        }
-
-        return rv;
-    }
-
-    /*
-     * Utility funcions
-     */
-    function assert(expected_true, function_name, description, error, substitutions)
-    {
-        if (expected_true !== true) {
-            var msg = make_message(function_name, description,
-                                   error, substitutions);
-            throw new AssertionError(msg);
-        }
-    }
-
-    function AssertionError(message)
-    {
-        this.message = message;
-        this.stack = this.get_stack();
-    }
-
-    AssertionError.prototype = Object.create(Error.prototype);
-
-    AssertionError.prototype.get_stack = function() {
-        var stack = new Error().stack;
-        // IE11 does not initialize 'Error.stack' until the object is thrown.
-        if (!stack) {
-            try {
-                throw new Error();
-            } catch (e) {
-                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(("-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++;
-        }
-
-        // Then skip the top frames originating from testharness.js to begin the stack at the test code.
-        while (re.test(lines[i]) && i < lines.length) {
-            i++;
-        }
-
-        // Paranoid check that we didn't skip all frames.  If so, return the original stack unmodified.
-        if (i >= lines.length) {
-            return stack;
-        }
-
-        return lines.slice(i).join("\n");
-    }
-
-    function make_message(function_name, description, error, substitutions)
-    {
-        for (var p in substitutions) {
-            if (substitutions.hasOwnProperty(p)) {
-                substitutions[p] = format_value(substitutions[p]);
-            }
-        }
-        var node_form = substitute(["{text}", "${function_name}: ${description}" + error],
-                                   merge({function_name:function_name,
-                                          description:(description?description + " ":"")},
-                                          substitutions));
-        return node_form.slice(1).join("");
-    }
-
-    function filter(array, callable, thisObj) {
-        var rv = [];
-        for (var i = 0; i < array.length; i++) {
-            if (array.hasOwnProperty(i)) {
-                var pass = callable.call(thisObj, array[i], i, array);
-                if (pass) {
-                    rv.push(array[i]);
-                }
-            }
-        }
-        return rv;
-    }
-
-    function map(array, callable, thisObj)
-    {
-        var rv = [];
-        rv.length = array.length;
-        for (var i = 0; i < array.length; i++) {
-            if (array.hasOwnProperty(i)) {
-                rv[i] = callable.call(thisObj, array[i], i, array);
-            }
-        }
-        return rv;
-    }
-
-    function extend(array, items)
-    {
-        Array.prototype.push.apply(array, items);
-    }
-
-    function forEach(array, callback, thisObj)
-    {
-        for (var i = 0; i < array.length; i++) {
-            if (array.hasOwnProperty(i)) {
-                callback.call(thisObj, array[i], i, array);
-            }
-        }
-    }
-
-    function merge(a,b)
-    {
-        var rv = {};
-        var p;
-        for (p in a) {
-            rv[p] = a[p];
-        }
-        for (p in b) {
-            rv[p] = b[p];
-        }
-        return rv;
-    }
-
-    function expose(object, name)
-    {
-        var components = name.split(".");
-        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;
-    }
-})();
-// vim: set expandtab shiftwidth=4 tabstop=4:
deleted file mode 100644
--- a/js/src/jit-test/lib/web-platform-testharness.js
+++ /dev/null
@@ -1,198 +0,0 @@
-// 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)}`);
-}
deleted file mode 100644
--- a/js/src/jit-test/tests/stream/bug-1512008.js
+++ /dev/null
@@ -1,6 +0,0 @@
-Object.defineProperty(Promise, Symbol.species, {
-  value: function(g) {
-    g(function() {}, function() {})
-  }
-});
-new ReadableStream().tee();
deleted file mode 100644
--- a/js/src/jit-test/tests/web-platform/streams/readable-streams/bad-strategies.js
+++ /dev/null
@@ -1,2 +0,0 @@
-load(libdir + "web-platform-testharness.js");
-load_web_platform_test("streams/readable-streams/bad-strategies.js");
deleted file mode 100644
--- a/js/src/jit-test/tests/web-platform/streams/readable-streams/bad-underlying-sources.js
+++ /dev/null
@@ -1,2 +0,0 @@
-load(libdir + "web-platform-testharness.js");
-load_web_platform_test("streams/readable-streams/bad-underlying-sources.js");
deleted file mode 100644
--- a/js/src/jit-test/tests/web-platform/streams/readable-streams/brand-checks.js
+++ /dev/null
@@ -1,2 +0,0 @@
-load(libdir + "web-platform-testharness.js");
-load_web_platform_test("streams/readable-streams/brand-checks.js");
deleted file mode 100644
--- a/js/src/jit-test/tests/web-platform/streams/readable-streams/cancel.js
+++ /dev/null
@@ -1,2 +0,0 @@
-load(libdir + "web-platform-testharness.js");
-load_web_platform_test("streams/readable-streams/cancel.js");
deleted file mode 100644
--- a/js/src/jit-test/tests/web-platform/streams/readable-streams/constructor.js
+++ /dev/null
@@ -1,2 +0,0 @@
-load(libdir + "web-platform-testharness.js");
-load_web_platform_test("streams/readable-streams/constructor.js");
deleted file mode 100644
--- a/js/src/jit-test/tests/web-platform/streams/readable-streams/count-queuing-strategy-integration.js
+++ /dev/null
@@ -1,2 +0,0 @@
-load(libdir + "web-platform-testharness.js");
-load_web_platform_test("streams/readable-streams/count-queuing-strategy-integration.js");
deleted file mode 100644
--- a/js/src/jit-test/tests/web-platform/streams/readable-streams/default-reader.js
+++ /dev/null
@@ -1,2 +0,0 @@
-load(libdir + "web-platform-testharness.js");
-load_web_platform_test("streams/readable-streams/default-reader.js");
deleted file mode 100644
--- a/js/src/jit-test/tests/web-platform/streams/readable-streams/floating-point-total-queue-size.js
+++ /dev/null
@@ -1,2 +0,0 @@
-load(libdir + "web-platform-testharness.js");
-load_web_platform_test("streams/readable-streams/floating-point-total-queue-size.js");
deleted file mode 100644
--- a/js/src/jit-test/tests/web-platform/streams/readable-streams/garbage-collection.js
+++ /dev/null
@@ -1,2 +0,0 @@
-load(libdir + "web-platform-testharness.js");
-load_web_platform_test("streams/readable-streams/garbage-collection.js");
deleted file mode 100644
--- a/js/src/jit-test/tests/web-platform/streams/readable-streams/general.js
+++ /dev/null
@@ -1,28 +0,0 @@
-// The web-platform test we're going to load (on the last line of this file)
-// contains many tests; among them, it checks for the existence of pipeTo and
-// pipeThrough, which we don't implement yet. Hack it:
-if (ReadableStream.prototype.pipeTo || ReadableStream.prototype.pipeThrough) {
-  throw new Error("Congratulations on implementing pipeTo/pipeThrough! Please update this test.\n");
-}
-// They don't have to work to pass the test, just exist:
-Object.defineProperties(ReadableStream.prototype, {
-  "pipeTo": {
-    configurable: true,
-    writable: true,
-    enumerable: false,
-    value: function pipeTo(dest, {preventClose, preventAbort, preventCancel, signal} = {}) {
-      throw new InternalError("pipeTo: not implemented");
-    },
-  },
-  "pipeThrough": {
-    configurable: true,
-    writable: true,
-    enumerable: false,
-    value: function pipeThrough({writable, readable}, {preventClose, preventAbort, preventCancel, signal}) {
-      throw new InternalError("pipeThrough: not implemented");
-    },
-  },
-});
-
-load(libdir + "web-platform-testharness.js");
-load_web_platform_test("streams/readable-streams/general.js");
deleted file mode 100644
--- a/js/src/jit-test/tests/web-platform/streams/readable-streams/patched-global.js
+++ /dev/null
@@ -1,2 +0,0 @@
-load(libdir + "web-platform-testharness.js");
-load_web_platform_test("streams/readable-streams/patched-global.js");
deleted file mode 100644
--- a/js/src/jit-test/tests/web-platform/streams/readable-streams/reentrant-strategies.js
+++ /dev/null
@@ -1,4 +0,0 @@
-// |jit-test| error: ReferenceError: WritableStream is not defined
-
-load(libdir + "web-platform-testharness.js");
-load(libdir + "../../../../testing/web-platform/tests/streams/readable-streams/reentrant-strategies.js");
deleted file mode 100644
--- a/js/src/jit-test/tests/web-platform/streams/readable-streams/tee.js
+++ /dev/null
@@ -1,2 +0,0 @@
-load(libdir + "web-platform-testharness.js");
-load_web_platform_test("streams/readable-streams/tee.js");
deleted file mode 100644
--- a/js/src/jit-test/tests/web-platform/streams/readable-streams/templated.js
+++ /dev/null
@@ -1,28 +0,0 @@
-// The web-platform test we're going to load (on the last line of this file)
-// contains many tests; among them, it checks for the existence of pipeTo and
-// pipeThrough, which we don't implement yet. Hack it:
-if (ReadableStream.prototype.pipeTo || ReadableStream.prototype.pipeThrough) {
-  throw new Error("Congratulations on implementing pipeTo/pipeThrough! Please update this test.\n");
-}
-// They don't have to work to pass the test, just exist:
-Object.defineProperties(ReadableStream.prototype, {
-  "pipeTo": {
-    configurable: true,
-    writable: true,
-    enumerable: false,
-    value: function pipeTo(dest, {preventClose, preventAbort, preventCancel, signal} = {}) {
-      throw new InternalError("pipeTo: not implemented");
-    },
-  },
-  "pipeThrough": {
-    configurable: true,
-    writable: true,
-    enumerable: false,
-    value: function pipeThrough({writable, readable}, {preventClose, preventAbort, preventCancel, signal}) {
-      throw new InternalError("pipeThrough: not implemented");
-    },
-  },
-});
-
-load(libdir + "web-platform-testharness.js");
-load_web_platform_test("streams/readable-streams/templated.js");
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4029,28 +4029,36 @@ JS_PUBLIC_API bool JS::ResolvePromise(JS
   return ResolveOrRejectPromise(cx, promiseObj, resolutionValue, false);
 }
 
 JS_PUBLIC_API bool JS::RejectPromise(JSContext* cx, JS::HandleObject promiseObj,
                                      JS::HandleValue rejectionValue) {
   return ResolveOrRejectPromise(cx, promiseObj, rejectionValue, true);
 }
 
-static MOZ_MUST_USE bool CallOriginalPromiseThenImpl(
-    JSContext* cx, JS::HandleObject promiseObj, JS::HandleObject onFulfilledObj,
-    JS::HandleObject onRejectedObj, JS::MutableHandleObject resultObj,
+static bool CallOriginalPromiseThenImpl(
+    JSContext* cx, JS::HandleObject promiseObj, JS::HandleObject onResolvedObj_,
+    JS::HandleObject onRejectedObj_, JS::MutableHandleObject resultObj,
     CreateDependentPromise createDependent) {
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
-  cx->check(promiseObj, onFulfilledObj, onRejectedObj);
-
-  MOZ_ASSERT_IF(onFulfilledObj, IsCallable(onFulfilledObj));
-  MOZ_ASSERT_IF(onRejectedObj, IsCallable(onRejectedObj));
-
-  RootedValue onFulfilled(cx, ObjectOrNullValue(onFulfilledObj));
+  cx->check(promiseObj, onResolvedObj_, onRejectedObj_);
+
+  MOZ_ASSERT_IF(onResolvedObj_, IsCallable(onResolvedObj_));
+  MOZ_ASSERT_IF(onRejectedObj_, IsCallable(onRejectedObj_));
+  RootedObject onResolvedObj(cx, onResolvedObj_);
+  RootedObject onRejectedObj(cx, onRejectedObj_);
+
+  if (IsWrapper(promiseObj) && !CheckedUnwrap(promiseObj)) {
+    ReportAccessDenied(cx);
+    return false;
+  }
+  MOZ_ASSERT(CheckedUnwrap(promiseObj)->is<PromiseObject>());
+
+  RootedValue onFulfilled(cx, ObjectOrNullValue(onResolvedObj));
   RootedValue onRejected(cx, ObjectOrNullValue(onRejectedObj));
   return OriginalPromiseThen(cx, promiseObj, onFulfilled, onRejected, resultObj,
                              createDependent);
 }
 
 JS_PUBLIC_API JSObject* JS::CallOriginalPromiseThen(
     JSContext* cx, JS::HandleObject promiseObj, JS::HandleObject onResolvedObj,
     JS::HandleObject onRejectedObj) {
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3451,43 +3451,39 @@ extern JS_PUBLIC_API bool ResolvePromise
 extern JS_PUBLIC_API bool RejectPromise(JSContext* cx,
                                         JS::HandleObject promiseObj,
                                         JS::HandleValue rejectionValue);
 
 /**
  * Calls the current compartment's original Promise.prototype.then on the
  * given `promise`, with `onResolve` and `onReject` passed as arguments.
  *
- * Throws a TypeError if `promise` isn't a Promise (or possibly a different
- * error if it's a security wrapper or dead object proxy).
- *
- * Asserts that `onFulfilled` and `onRejected` are each either callable or
- * null.
+ * Asserts if the passed-in `promise` object isn't an unwrapped instance of
+ * `Promise` or a subclass or `onResolve` and `onReject` aren't both either
+ * `nullptr` or callable objects.
  */
 extern JS_PUBLIC_API JSObject* CallOriginalPromiseThen(
-    JSContext* cx, JS::HandleObject promise, JS::HandleObject onFulfilled,
-    JS::HandleObject onRejected);
+    JSContext* cx, JS::HandleObject promise, JS::HandleObject onResolve,
+    JS::HandleObject onReject);
 
 /**
  * Unforgeable, optimized version of the JS builtin Promise.prototype.then.
  *
  * Takes a Promise instance and `onResolve`, `onReject` callables to enqueue
  * as reactions for that promise. In difference to Promise.prototype.then,
  * this doesn't create and return a new Promise instance.
  *
- * Throws a TypeError if `promise` isn't a Promise (or possibly a different
- * error if it's a security wrapper or dead object proxy).
- *
- * Asserts that `onFulfilled` and `onRejected` are each either callable or
- * null.
+ * Asserts if the passed-in `promise` object isn't an unwrapped instance of
+ * `Promise` or a subclass or `onResolve` and `onReject` aren't both callable
+ * objects.
  */
 extern JS_PUBLIC_API bool AddPromiseReactions(JSContext* cx,
                                               JS::HandleObject promise,
-                                              JS::HandleObject onFulfilled,
-                                              JS::HandleObject onRejected);
+                                              JS::HandleObject onResolve,
+                                              JS::HandleObject onReject);
 
 // This enum specifies whether a promise is expected to keep track of
 // information that is useful for embedders to implement user activation
 // behavior handling as specified in the HTML spec:
 // https://html.spec.whatwg.org/multipage/interaction.html#triggered-by-user-activation
 // By default, promises created by SpiderMonkey do not make any attempt to keep
 // track of information about whether an activation behavior was being processed
 // when the original promise in a promise chain was created.  If the embedder
--- a/js/src/vm/Compartment-inl.h
+++ b/js/src/vm/Compartment-inl.h
@@ -113,107 +113,122 @@ namespace detail {
  * Return the name of class T as a static null-terminated ASCII string constant
  * (for error messages).
  */
 template <class T>
 const char* ClassName() {
   return T::class_.name;
 }
 
-template <class T, class ErrorCallback>
-MOZ_MUST_USE T* UnwrapAndTypeCheckValueSlowPath(JSContext* cx,
-                                                HandleValue value,
-                                                ErrorCallback throwTypeError) {
+template <class T>
+MOZ_MUST_USE T* UnwrapAndTypeCheckThisSlowPath(JSContext* cx, HandleValue val,
+                                               const char* methodName) {
+  if (!val.isObject()) {
+    JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
+                               JSMSG_INCOMPATIBLE_PROTO, ClassName<T>(),
+                               methodName, InformalValueTypeName(val));
+    return nullptr;
+  }
+
+  JSObject* obj = &val.toObject();
+  if (IsWrapper(obj)) {
+    obj = CheckedUnwrap(obj);
+    if (!obj) {
+      ReportAccessDenied(cx);
+      return nullptr;
+    }
+  }
+
+  if (!obj->is<T>()) {
+    JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
+                               JSMSG_INCOMPATIBLE_PROTO, ClassName<T>(),
+                               methodName, InformalValueTypeName(val));
+    return nullptr;
+  }
+
+  return &obj->as<T>();
+}
+
+template <class T>
+MOZ_MUST_USE T* UnwrapAndTypeCheckArgumentSlowPath(JSContext* cx,
+                                                   CallArgs& args,
+                                                   const char* methodName,
+                                                   int argIndex) {
+  Value val = args.get(argIndex);
   JSObject* obj = nullptr;
-  if (value.isObject()) {
-    obj = &value.toObject();
+  if (val.isObject()) {
+    obj = &val.toObject();
     if (IsWrapper(obj)) {
       obj = CheckedUnwrap(obj);
       if (!obj) {
         ReportAccessDenied(cx);
         return nullptr;
       }
     }
   }
 
   if (!obj || !obj->is<T>()) {
-    throwTypeError();
+    ToCStringBuf cbuf;
+    if (char* numStr = NumberToCString(cx, &cbuf, argIndex + 1, 10)) {
+      JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
+                                 JSMSG_WRONG_TYPE_ARG, numStr, methodName,
+                                 ClassName<T>(), InformalValueTypeName(val));
+    }
     return nullptr;
   }
 
   return &obj->as<T>();
 }
 
 }  // namespace detail
 
 /**
- * Remove all wrappers from `val` and try to downcast the result to class `T`.
- *
- * DANGER: The result may not be same-compartment with `cx`.
- *
- * This calls `throwTypeError` if the value isn't an object, cannot be
- * unwrapped, or isn't an instance of the expected type. `throwTypeError` must
- * in fact throw a TypeError (or OOM trying).
- */
-template <class T, class ErrorCallback>
-inline MOZ_MUST_USE T* UnwrapAndTypeCheckValue(JSContext* cx, HandleValue value,
-                                               ErrorCallback throwTypeError) {
-  static_assert(!std::is_convertible<T*, Wrapper*>::value,
-                "T can't be a Wrapper type; this function discards wrappers");
-  cx->check(value);
-  if (value.isObject() && value.toObject().is<T>()) {
-    return &value.toObject().as<T>();
-  }
-  return detail::UnwrapAndTypeCheckValueSlowPath<T>(cx, value, throwTypeError);
-}
-
-/**
- * Remove all wrappers from `args.thisv()` and try to downcast the result to
- * class `T`.
+ * Remove all wrappers from `val` and try to downcast the result to `T`.
  *
  * DANGER: The result may not be same-compartment with `cx`.
  *
  * This throws a TypeError if the value isn't an object, cannot be unwrapped,
  * or isn't an instance of the expected type.
  */
 template <class T>
 inline MOZ_MUST_USE T* UnwrapAndTypeCheckThis(JSContext* cx, CallArgs& args,
                                               const char* methodName) {
+  static_assert(!std::is_convertible<T*, Wrapper*>::value,
+                "T can't be a Wrapper type; this function discards wrappers");
+
   HandleValue thisv = args.thisv();
-  return UnwrapAndTypeCheckValue<T>(cx, thisv, [cx, methodName, thisv] {
-    JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
-                               JSMSG_INCOMPATIBLE_PROTO, detail::ClassName<T>(),
-                               methodName, InformalValueTypeName(thisv));
-  });
+  cx->check(thisv);
+  if (thisv.isObject() && thisv.toObject().is<T>()) {
+    return &thisv.toObject().as<T>();
+  }
+  return detail::UnwrapAndTypeCheckThisSlowPath<T>(cx, thisv, methodName);
 }
 
 /**
  * Remove all wrappers from `args[argIndex]` and try to downcast the result to
  * class `T`.
  *
  * DANGER: The result may not be same-compartment with `cx`.
  *
  * This throws a TypeError if the specified argument is missing, isn't an
  * object, cannot be unwrapped, or isn't an instance of the expected type.
  */
 template <class T>
 inline MOZ_MUST_USE T* UnwrapAndTypeCheckArgument(JSContext* cx, CallArgs& args,
                                                   const char* methodName,
                                                   int argIndex) {
-  HandleValue val = args.get(argIndex);
-  return UnwrapAndTypeCheckValue<T>(cx, val, [cx, val, methodName, argIndex] {
-    ToCStringBuf cbuf;
-    if (char* numStr = NumberToCString(cx, &cbuf, argIndex + 1, 10)) {
-      JS_ReportErrorNumberLatin1(
-          cx, GetErrorMessage, nullptr, JSMSG_WRONG_TYPE_ARG, numStr,
-          methodName, detail::ClassName<T>(), InformalValueTypeName(val));
-    } else {
-      ReportOutOfMemory(cx);
-    }
-  });
+  static_assert(!std::is_convertible<T*, Wrapper*>::value,
+                "T can't be a Wrapper type; this function discards wrappers");
+
+  Value val = args.get(argIndex);
+  if (val.isObject() && val.toObject().is<T>()) {
+    return &val.toObject().as<T>();
+  }
+  return detail::UnwrapAndTypeCheckArgumentSlowPath<T>(cx, args, methodName,
+                                                       argIndex);
 }
 
 /**
  * Unwrap an object of a known type.
  *
  * If `obj` is an object of class T, this returns a pointer to that object. If
  * `obj` is a wrapper for such an object, this tries to unwrap the object and
  * return a pointer to it. If access is denied, or `obj` was a wrapper but has
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/streams/readable-streams/bad-underlying-sources.dedicatedworker.html.ini
@@ -0,0 +1,16 @@
+[bad-underlying-sources.dedicatedworker.html]
+  [Underlying source: throwing pull getter (initial pull)]
+    expected: FAIL
+
+  [Underlying source pull: throwing getter (second pull does not result in a second get)]
+    expected: FAIL
+
+  [Underlying source cancel: throwing getter]
+    expected: FAIL
+
+  [Underlying source: calling error twice should not throw]
+    expected: FAIL
+
+  [Underlying source: calling error after close should not throw]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/streams/readable-streams/bad-underlying-sources.html.ini
@@ -0,0 +1,16 @@
+[bad-underlying-sources.html]
+  [Underlying source: throwing pull getter (initial pull)]
+    expected: FAIL
+
+  [Underlying source pull: throwing getter (second pull does not result in a second get)]
+    expected: FAIL
+
+  [Underlying source cancel: throwing getter]
+    expected: FAIL
+
+  [Underlying source: calling error twice should not throw]
+    expected: FAIL
+
+  [Underlying source: calling error after close should not throw]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/streams/readable-streams/bad-underlying-sources.serviceworker.https.html.ini
@@ -0,0 +1,16 @@
+[bad-underlying-sources.serviceworker.https.html]
+  [Underlying source: throwing pull getter (initial pull)]
+    expected: FAIL
+
+  [Underlying source pull: throwing getter (second pull does not result in a second get)]
+    expected: FAIL
+
+  [Underlying source cancel: throwing getter]
+    expected: FAIL
+
+  [Underlying source: calling error twice should not throw]
+    expected: FAIL
+
+  [Underlying source: calling error after close should not throw]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/streams/readable-streams/bad-underlying-sources.sharedworker.html.ini
@@ -0,0 +1,16 @@
+[bad-underlying-sources.sharedworker.html]
+  [Underlying source: throwing pull getter (initial pull)]
+    expected: FAIL
+
+  [Underlying source pull: throwing getter (second pull does not result in a second get)]
+    expected: FAIL
+
+  [Underlying source cancel: throwing getter]
+    expected: FAIL
+
+  [Underlying source: calling error twice should not throw]
+    expected: FAIL
+
+  [Underlying source: calling error after close should not throw]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/streams/readable-streams/constructor.dedicatedworker.html.ini
@@ -0,0 +1,19 @@
+[constructor.dedicatedworker.html]
+  [ReadableStream constructor should stop after get on pull fails]
+    expected: FAIL
+
+  [ReadableStream constructor should stop after validate on pull fails]
+    expected: FAIL
+
+  [ReadableStream constructor should stop after get on cancel fails]
+    expected: FAIL
+
+  [ReadableStream constructor should stop after validate on cancel fails]
+    expected: FAIL
+
+  [ReadableStream constructor should stop after get on start fails]
+    expected: FAIL
+
+  [ReadableStream constructor should stop after validate on start fails]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/streams/readable-streams/constructor.html.ini
@@ -0,0 +1,19 @@
+[constructor.html]
+  [ReadableStream constructor should stop after get on pull fails]
+    expected: FAIL
+
+  [ReadableStream constructor should stop after validate on pull fails]
+    expected: FAIL
+
+  [ReadableStream constructor should stop after get on cancel fails]
+    expected: FAIL
+
+  [ReadableStream constructor should stop after validate on cancel fails]
+    expected: FAIL
+
+  [ReadableStream constructor should stop after get on start fails]
+    expected: FAIL
+
+  [ReadableStream constructor should stop after validate on start fails]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/streams/readable-streams/constructor.serviceworker.https.html.ini
@@ -0,0 +1,19 @@
+[constructor.serviceworker.https.html]
+  [ReadableStream constructor should stop after get on pull fails]
+    expected: FAIL
+
+  [ReadableStream constructor should stop after validate on pull fails]
+    expected: FAIL
+
+  [ReadableStream constructor should stop after get on cancel fails]
+    expected: FAIL
+
+  [ReadableStream constructor should stop after validate on cancel fails]
+    expected: FAIL
+
+  [ReadableStream constructor should stop after get on start fails]
+    expected: FAIL
+
+  [ReadableStream constructor should stop after validate on start fails]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/streams/readable-streams/constructor.sharedworker.html.ini
@@ -0,0 +1,19 @@
+[constructor.sharedworker.html]
+  [ReadableStream constructor should stop after get on pull fails]
+    expected: FAIL
+
+  [ReadableStream constructor should stop after validate on pull fails]
+    expected: FAIL
+
+  [ReadableStream constructor should stop after get on cancel fails]
+    expected: FAIL
+
+  [ReadableStream constructor should stop after validate on cancel fails]
+    expected: FAIL
+
+  [ReadableStream constructor should stop after get on start fails]
+    expected: FAIL
+
+  [ReadableStream constructor should stop after validate on start fails]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/streams/readable-streams/garbage-collection.dedicatedworker.html.ini
@@ -0,0 +1,4 @@
+[garbage-collection.dedicatedworker.html]
+  [ReadableStreamController methods should continue working properly when scripts lose their reference to the readable stream]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/streams/readable-streams/garbage-collection.html.ini
@@ -0,0 +1,4 @@
+[garbage-collection.html]
+  [ReadableStreamController methods should continue working properly when scripts lose their reference to the readable stream]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/streams/readable-streams/garbage-collection.serviceworker.https.html.ini
@@ -0,0 +1,4 @@
+[garbage-collection.serviceworker.https.html]
+  [ReadableStreamController methods should continue working properly when scripts lose their reference to the readable stream]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/streams/readable-streams/garbage-collection.sharedworker.html.ini
@@ -0,0 +1,4 @@
+[garbage-collection.sharedworker.html]
+  [ReadableStreamController methods should continue working properly when scripts lose their reference to the readable stream]
+    expected: FAIL
+
--- a/testing/web-platform/meta/streams/readable-streams/general.dedicatedworker.html.ini
+++ b/testing/web-platform/meta/streams/readable-streams/general.dedicatedworker.html.ini
@@ -1,4 +1,10 @@
 [general.dedicatedworker.html]
   [ReadableStream instances should have the correct list of properties]
     expected: FAIL
 
+  [ReadableStream constructor will not tolerate initial garbage as cancel argument]
+    expected: FAIL
+
+  [ReadableStream constructor will not tolerate initial garbage as pull argument]
+    expected: FAIL
+
--- a/testing/web-platform/meta/streams/readable-streams/general.html.ini
+++ b/testing/web-platform/meta/streams/readable-streams/general.html.ini
@@ -1,4 +1,10 @@
 [general.html]
   [ReadableStream instances should have the correct list of properties]
     expected: FAIL
 
+  [ReadableStream constructor will not tolerate initial garbage as cancel argument]
+    expected: FAIL
+
+  [ReadableStream constructor will not tolerate initial garbage as pull argument]
+    expected: FAIL
+
--- a/testing/web-platform/meta/streams/readable-streams/general.serviceworker.https.html.ini
+++ b/testing/web-platform/meta/streams/readable-streams/general.serviceworker.https.html.ini
@@ -1,4 +1,10 @@
 [general.serviceworker.https.html]
   [ReadableStream instances should have the correct list of properties]
     expected: FAIL
 
+  [ReadableStream constructor will not tolerate initial garbage as cancel argument]
+    expected: FAIL
+
+  [ReadableStream constructor will not tolerate initial garbage as pull argument]
+    expected: FAIL
+
--- a/testing/web-platform/meta/streams/readable-streams/general.sharedworker.html.ini
+++ b/testing/web-platform/meta/streams/readable-streams/general.sharedworker.html.ini
@@ -1,4 +1,10 @@
 [general.sharedworker.html]
   [ReadableStream instances should have the correct list of properties]
     expected: FAIL
 
+  [ReadableStream constructor will not tolerate initial garbage as cancel argument]
+    expected: FAIL
+
+  [ReadableStream constructor will not tolerate initial garbage as pull argument]
+    expected: FAIL
+