Bug 1507943 - Fix ReadableStream constructor handling of "pull" and "cancel" methods. r=arai
☠☠ backed out by 7eb42458e2d8 ☠ ☠
authorJason Orendorff <jorendorff@mozilla.com>
Fri, 07 Dec 2018 20:03:16 +0000
changeset 508849 345ad3e746e8e50d2365f6dac8ef5be6383d1afd
parent 508848 b2a0ae5e511537db7d316ce5a8de1b739290595b
child 508850 bc83a2fe5c178f5f555b5ff4566b387a028eccdc
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersarai
bugs1507943
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1507943 - Fix ReadableStream constructor handling of "pull" and "cancel" methods. r=arai Differential Revision: https://phabricator.services.mozilla.com/D13747
js/src/builtin/Stream.cpp
js/src/builtin/Stream.h
js/src/jit-test/tests/web-platform/streams/readable-streams/constructor.js
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/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/js/src/builtin/Stream.cpp
+++ b/js/src/builtin/Stream.cpp
@@ -4,16 +4,17 @@
  * 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;
@@ -116,20 +117,16 @@ 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;
@@ -465,19 +462,31 @@ 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, HandleValue underlyingSource,
-    double highWaterMarkVal, HandleValue size);
+    JSContext* cx, Handle<ReadableStream*> stream, SourceAlgorithms algorithms,
+    HandleValue underlyingSource, HandleValue pullMethod,
+    HandleValue cancelMethod, double highWaterMark, HandleValue size);
 
 static MOZ_MUST_USE ReadableByteStreamController*
 CreateExternalReadableByteStreamController(
     JSContext* cx, Handle<ReadableStream*> stream,
     JS::ReadableStreamUnderlyingSource* source);
 
 ReadableStream* ReadableStream::createExternalSourceStream(
     JSContext* cx, JS::ReadableStreamUnderlyingSource* source,
@@ -498,16 +507,21 @@ 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;
@@ -600,18 +614,18 @@ bool ReadableStream::constructor(JSConte
                                              &highWaterMark)) {
         return false;
       }
     }
 
     // Step 7.d: Perform
     //           ? SetUpReadableStreamDefaultControllerFromUnderlyingSource(
     //           this, underlyingSource, highWaterMark, sizeAlgorithm).
-    if (!SetUpReadableStreamDefaultController(cx, stream, underlyingSource,
-                                              highWaterMark, size)) {
+    if (!SetUpReadableStreamDefaultControllerFromUnderlyingSource(
+            cx, stream, underlyingSource, highWaterMark, size)) {
       return false;
     }
 
     args.rval().setObject(*stream);
     return true;
   }
 
   // Step 8: Otherwise, throw a RangeError exception.
@@ -806,21 +820,24 @@ 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 as a single
- * underlyingSource argument; see SetUpReadableStreamDefaultController().
+ * The start/pull/cancelAlgorithm arguments are represented instead as four
+ * arguments: sourceAlgorithms, underlyingSource, pullMethod, cancelMethod.
+ * See the comment on SetUpReadableStreamDefaultController.
  */
 MOZ_MUST_USE ReadableStream* CreateReadableStream(
-    JSContext* cx, HandleValue underlyingSource, double highWaterMark = 1,
+    JSContext* cx, SourceAlgorithms sourceAlgorithms,
+    HandleValue underlyingSource, HandleValue pullMethod = UndefinedHandleValue,
+    HandleValue cancelMethod = UndefinedHandleValue, 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)
@@ -834,18 +851,20 @@ 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, underlyingSource,
-                                            highWaterMark, sizeAlgorithm)) {
+
+  if (!SetUpReadableStreamDefaultController(
+          cx, stream, sourceAlgorithms, underlyingSource, pullMethod,
+          cancelMethod, highWaterMark, sizeAlgorithm)) {
     return nullptr;
   }
 
   // Step 8: Return stream.
   return stream;
 }
 
 // Streams spec, 3.3.4. CreateReadableByteStream (
@@ -1245,30 +1264,32 @@ 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, underlyingSource));
+  branch1Stream.set(
+      CreateReadableStream(cx, SourceAlgorithms::Tee, 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, underlyingSource));
+  branch2Stream.set(
+      CreateReadableStream(cx, SourceAlgorithms::Tee, underlyingSource));
   if (!branch2Stream) {
     return false;
   }
 
   Rooted<ReadableStreamDefaultController*> branch2(cx);
   branch2 = &branch2Stream->controller()->as<ReadableStreamDefaultController>();
   branch2->setTeeBranch2();
   teeState->setBranch2(branch2);
@@ -2441,16 +2462,22 @@ static const JSPropertySpec ReadableStre
 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(
@@ -2482,66 +2509,94 @@ 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:
-  // 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.
+  // 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);
   if (IsMaybeWrapped<TeeState>(unwrappedUnderlyingSource)) {
-    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()) {
+    // 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.
     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)) {
-      return nullptr;
+      result = nullptr;
+    } else {
+      result = PromiseObject::unforgeableResolve(cx, rval);
     }
-    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);
+  } 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;
 }
 
 inline static MOZ_MUST_USE bool DequeueValue(
     JSContext* cx, Handle<ReadableStreamController*> unwrappedContainer,
     MutableHandleValue chunk);
 
 /**
  * Streams spec, 3.8.5.2. ReadableStreamDefaultController [[PullSteps]](
@@ -2694,17 +2749,18 @@ 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
-  //         ! ReadableByteStreamControllerShouldCallPull(controller).
+  //         ! ReadableStreamDefaultControllerShouldCallPull(controller).
+  // (ReadableByteStreamDefaultControllerShouldCallPull in 3.12.3.)
   bool shouldPull = ReadableStreamControllerShouldCallPull(unwrappedController);
 
   // Step 2: If shouldPull is false, return.
   if (!shouldPull) {
     return true;
   }
 
   // Step 3: If controller.[[pulling]] is true,
@@ -2717,53 +2773,80 @@ 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();
 
-  // Step 6: Let pullPromise be
-  //         ! PromiseInvokeOrNoop(controller.[[underlyingByteSource]],
-  //                               "pull", controller).
+  // We use this variable in step 7. For ease of error-handling, we wrap it
+  // early.
   RootedObject wrappedController(cx, unwrappedController);
   if (!cx->compartment()->wrap(cx, &wrappedController)) {
     return false;
   }
-  RootedValue controllerVal(cx, ObjectValue(*wrappedController));
+
+  // 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 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 {
-    RootedValue underlyingSource(cx, unwrappedUnderlyingSource);
-    if (!cx->compartment()->wrap(cx, &underlyingSource)) {
-      return false;
+    // 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;
+      }
     }
-    pullPromise = PromiseInvokeOrNoop(cx, underlyingSource, cx->names().pull,
-                                      controllerVal);
   }
   if (!pullPromise) {
     return false;
   }
 
   RootedObject onPullFulfilled(
       cx, NewHandler(cx, ControllerPullHandler, wrappedController));
   if (!onPullFulfilled) {
@@ -2826,16 +2909,38 @@ 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());
 
@@ -3021,28 +3126,54 @@ 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.
  *
- * 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).
+ * 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.
  *
  * 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, HandleValue underlyingSource,
-    double highWaterMark, HandleValue size) {
+    JSContext* cx, Handle<ReadableStream*> stream,
+    SourceAlgorithms sourceAlgorithms, HandleValue underlyingSource,
+    HandleValue pullMethod, HandleValue cancelMethod, 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;
@@ -3065,33 +3196,30 @@ 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.)
+  controller->setUnderlyingSource(underlyingSource);
+  controller->setPullMethod(pullMethod);
+
   // 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->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 (!underlyingSource.isObject() ||
-      !underlyingSource.toObject().is<TeeState>()) {
+  if (sourceAlgorithms == SourceAlgorithms::Script) {
     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.
@@ -3118,16 +3246,71 @@ 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 )
@@ -4023,16 +4206,74 @@ 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);
 
@@ -4049,40 +4290,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, obsolete (previously 6.4.3) PromiseInvokeOrNoop ( O, P, args )
- * Specialized to one arg, because that's what all stream related callers use.
+ * Streams spec, 6.3.5. PromiseCall ( F, V, args )
+ * As it happens, all callers pass exactly one argument.
  */
-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)) {
+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]].
     return PromiseRejectedWithPendingError(cx);
   }
 
   // Step 6: Otherwise, return a promise resolved with returnValue.[[Value]].
-  return PromiseObject::unforgeableResolve(cx, returnValue);
+  return PromiseObject::unforgeableResolve(cx, rval);
 }
 
 /**
  * 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).
@@ -4148,26 +4389,40 @@ 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);
 
-  RootedObject source(cx, underlyingSource);
-  if (!source) {
-    source = NewBuiltinClassInstance<PlainObject>(cx);
+  // 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);
     if (!source) {
       return nullptr;
     }
-  }
-  RootedValue sourceVal(cx, ObjectValue(*source));
+    sourceVal.setObject(*source);
+  }
   RootedValue sizeVal(cx, size ? ObjectValue(*size) : UndefinedValue());
-  return CreateReadableStream(cx, sourceVal, highWaterMark, sizeVal);
+
+  if (!SetUpReadableStreamDefaultControllerFromUnderlyingSource(
+          cx, stream, sourceVal, highWaterMark, sizeVal)) {
+    return nullptr;
+  }
+
+  return stream;
 }
 
 JS_PUBLIC_API JSObject* JS::NewReadableExternalSourceStreamObject(
     JSContext* cx, JS::ReadableStreamUnderlyingSource* underlyingSource,
     HandleObject proto /* = nullptr */) {
   MOZ_ASSERT(!cx->zone()->isAtomsZone());
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
--- a/js/src/builtin/Stream.h
+++ b/js/src/builtin/Stream.h
@@ -237,29 +237,44 @@ class StreamController : public NativeOb
 };
 
 class ReadableStreamController : public StreamController {
  public:
   /**
    * Memory layout for ReadableStream controllers, starting after the slots
    * reserved for queue container usage.
    *
-   * 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.
+   * 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.
    *
-   * 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.
+   * -   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.
    *
    * 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,
@@ -276,16 +291,24 @@ 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());
   }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/web-platform/streams/readable-streams/constructor.js
@@ -0,0 +1,2 @@
+load(libdir + "web-platform-testharness.js");
+load_web_platform_test("streams/readable-streams/constructor.js");
--- a/testing/web-platform/meta/streams/readable-streams/bad-underlying-sources.dedicatedworker.html.ini
+++ b/testing/web-platform/meta/streams/readable-streams/bad-underlying-sources.dedicatedworker.html.ini
@@ -1,16 +1,7 @@
 [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
 
--- a/testing/web-platform/meta/streams/readable-streams/bad-underlying-sources.html.ini
+++ b/testing/web-platform/meta/streams/readable-streams/bad-underlying-sources.html.ini
@@ -1,16 +1,7 @@
 [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
 
--- a/testing/web-platform/meta/streams/readable-streams/bad-underlying-sources.serviceworker.https.html.ini
+++ b/testing/web-platform/meta/streams/readable-streams/bad-underlying-sources.serviceworker.https.html.ini
@@ -1,16 +1,7 @@
 [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
 
--- a/testing/web-platform/meta/streams/readable-streams/bad-underlying-sources.sharedworker.html.ini
+++ b/testing/web-platform/meta/streams/readable-streams/bad-underlying-sources.sharedworker.html.ini
@@ -1,16 +1,7 @@
 [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
 
deleted file mode 100644
--- a/testing/web-platform/meta/streams/readable-streams/constructor.dedicatedworker.html.ini
+++ /dev/null
@@ -1,19 +0,0 @@
-[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
-
deleted file mode 100644
--- a/testing/web-platform/meta/streams/readable-streams/constructor.html.ini
+++ /dev/null
@@ -1,19 +0,0 @@
-[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
-
deleted file mode 100644
--- a/testing/web-platform/meta/streams/readable-streams/constructor.serviceworker.https.html.ini
+++ /dev/null
@@ -1,19 +0,0 @@
-[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
-
deleted file mode 100644
--- a/testing/web-platform/meta/streams/readable-streams/constructor.sharedworker.html.ini
+++ /dev/null
@@ -1,19 +0,0 @@
-[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
-
--- 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,10 +1,4 @@
 [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,10 +1,4 @@
 [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,10 +1,4 @@
 [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,10 +1,4 @@
 [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
-