11cb0ef41Sopenharmony_ci<!doctype html>
21cb0ef41Sopenharmony_ci<meta charset="utf-8">
31cb0ef41Sopenharmony_ci<title>Ensure Stream objects are created in expected globals. </title>
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ci<script src="/resources/testharness.js"></script>
61cb0ef41Sopenharmony_ci<script src="/resources/testharnessreport.js"></script>
71cb0ef41Sopenharmony_ci
81cb0ef41Sopenharmony_ci<body></body>
91cb0ef41Sopenharmony_ci<script>
101cb0ef41Sopenharmony_ci// These tests are loosely derived from Gecko's readable-stream-globals.js,
111cb0ef41Sopenharmony_ci// which is a test case designed around the JS Streams implementation.
121cb0ef41Sopenharmony_ci//
131cb0ef41Sopenharmony_ci// Unlike in JS Streams, where function calls switch realms and change
141cb0ef41Sopenharmony_ci// the resulting global of the resulting objects, in WebIDL streams,
151cb0ef41Sopenharmony_ci// the global of an object is (currently underspecified, but) intended
161cb0ef41Sopenharmony_ci// to be the "Relevant Global" of the 'this' object.
171cb0ef41Sopenharmony_ci//
181cb0ef41Sopenharmony_ci// See:
191cb0ef41Sopenharmony_ci// https://html.spec.whatwg.org/multipage/webappapis.html#relevant
201cb0ef41Sopenharmony_ci// https://github.com/whatwg/streams/issues/1213
211cb0ef41Sopenharmony_ci"use strict"
221cb0ef41Sopenharmony_ci
231cb0ef41Sopenharmony_ciconst iframe = document.createElement("iframe")
241cb0ef41Sopenharmony_cidocument.body.append(iframe)
251cb0ef41Sopenharmony_ci
261cb0ef41Sopenharmony_ciconst otherGlobal = iframe.contentWindow;
271cb0ef41Sopenharmony_ciconst OtherReadableStream = otherGlobal.ReadableStream
281cb0ef41Sopenharmony_ciconst OtherReadableStreamDefaultReader = otherGlobal.ReadableStreamDefaultReader;
291cb0ef41Sopenharmony_ciconst OtherReadableStreamDefaultController = otherGlobal.ReadableStreamDefaultController;
301cb0ef41Sopenharmony_ci
311cb0ef41Sopenharmony_cipromise_test(async () => {
321cb0ef41Sopenharmony_ci
331cb0ef41Sopenharmony_ci    // Controllers
341cb0ef41Sopenharmony_ci    let controller;
351cb0ef41Sopenharmony_ci    let otherController;
361cb0ef41Sopenharmony_ci
371cb0ef41Sopenharmony_ci    // Get Stream Prototypes and controllers.
381cb0ef41Sopenharmony_ci    let streamController;
391cb0ef41Sopenharmony_ci    let stream = new ReadableStream({start(c) { streamController = c; }});
401cb0ef41Sopenharmony_ci
411cb0ef41Sopenharmony_ci    const callReaderThisGlobal = OtherReadableStream.prototype.getReader.call(stream);
421cb0ef41Sopenharmony_ci    const newReaderOtherGlobal = new OtherReadableStreamDefaultReader(new ReadableStream());
431cb0ef41Sopenharmony_ci
441cb0ef41Sopenharmony_ci    // Relevant Global Checking.
451cb0ef41Sopenharmony_ci    assert_equals(callReaderThisGlobal instanceof ReadableStreamDefaultReader, true, "reader was created in this global (.call)");
461cb0ef41Sopenharmony_ci    assert_equals(newReaderOtherGlobal instanceof ReadableStreamDefaultReader, false, "reader was created in other global (new)");
471cb0ef41Sopenharmony_ci
481cb0ef41Sopenharmony_ci    assert_equals(callReaderThisGlobal instanceof OtherReadableStreamDefaultReader, false, "reader isn't coming from other global (.call)" );
491cb0ef41Sopenharmony_ci    assert_equals(newReaderOtherGlobal instanceof OtherReadableStreamDefaultReader, true, "reader isn't coming from other global (new)");
501cb0ef41Sopenharmony_ci
511cb0ef41Sopenharmony_ci    assert_equals(otherController instanceof ReadableStreamDefaultController, false, "otherController should come from other gloal")
521cb0ef41Sopenharmony_ci
531cb0ef41Sopenharmony_ci
541cb0ef41Sopenharmony_ci    const request = callReaderThisGlobal.read();
551cb0ef41Sopenharmony_ci    assert_equals(request instanceof Promise, true, "Promise comes from this global");
561cb0ef41Sopenharmony_ci
571cb0ef41Sopenharmony_ci    streamController.close();
581cb0ef41Sopenharmony_ci    const requestResult = await request;
591cb0ef41Sopenharmony_ci    assert_equals(requestResult instanceof Object, true, "returned object comes from this global");
601cb0ef41Sopenharmony_ci}, "Stream objects created in expected globals")
611cb0ef41Sopenharmony_ci
621cb0ef41Sopenharmony_cipromise_test(async () => {
631cb0ef41Sopenharmony_ci    const stream = new ReadableStream();
641cb0ef41Sopenharmony_ci    const otherReader = new OtherReadableStreamDefaultReader(stream);
651cb0ef41Sopenharmony_ci    const cancelPromise = ReadableStreamDefaultReader.prototype.cancel.call(otherReader);
661cb0ef41Sopenharmony_ci    assert_equals(cancelPromise instanceof Promise, true, "Cancel promise comes from the same global as the stream");
671cb0ef41Sopenharmony_ci    assert_equals(await cancelPromise, undefined, "Cancel promise resolves to undefined");
681cb0ef41Sopenharmony_ci}, "Cancel promise is created in same global as stream")
691cb0ef41Sopenharmony_ci
701cb0ef41Sopenharmony_ci// Refresh the streams and controllers.
711cb0ef41Sopenharmony_cifunction getFreshInstances() {
721cb0ef41Sopenharmony_ci    let controller;
731cb0ef41Sopenharmony_ci    let otherController;
741cb0ef41Sopenharmony_ci    let stream = new ReadableStream({
751cb0ef41Sopenharmony_ci        start(c) {
761cb0ef41Sopenharmony_ci            controller = c;
771cb0ef41Sopenharmony_ci        }
781cb0ef41Sopenharmony_ci    });
791cb0ef41Sopenharmony_ci
801cb0ef41Sopenharmony_ci    new OtherReadableStream({
811cb0ef41Sopenharmony_ci        start(c) {
821cb0ef41Sopenharmony_ci            otherController = c;
831cb0ef41Sopenharmony_ci        }
841cb0ef41Sopenharmony_ci    });
851cb0ef41Sopenharmony_ci
861cb0ef41Sopenharmony_ci    return {stream, controller, otherController}
871cb0ef41Sopenharmony_ci}
881cb0ef41Sopenharmony_ci
891cb0ef41Sopenharmony_ci
901cb0ef41Sopenharmony_cipromise_test(async () => {
911cb0ef41Sopenharmony_ci    // Test closed promise on reader from another global (connected to a this-global stream)
921cb0ef41Sopenharmony_ci    const {stream, controller, otherController} = getFreshInstances();
931cb0ef41Sopenharmony_ci
941cb0ef41Sopenharmony_ci    const otherReader = new OtherReadableStreamDefaultReader(stream);
951cb0ef41Sopenharmony_ci    const closedPromise = otherReader.closed;
961cb0ef41Sopenharmony_ci    assert_equals(closedPromise instanceof otherGlobal.Promise, true, "Closed promise in other global.");
971cb0ef41Sopenharmony_ci}, "Closed Promise in correct global");
981cb0ef41Sopenharmony_ci
991cb0ef41Sopenharmony_cipromise_test(async () => {
1001cb0ef41Sopenharmony_ci    const {stream, controller, otherController} = getFreshInstances();
1011cb0ef41Sopenharmony_ci
1021cb0ef41Sopenharmony_ci    const otherReader = OtherReadableStream.prototype.getReader.call(stream);
1031cb0ef41Sopenharmony_ci    assert_equals(otherReader instanceof ReadableStreamDefaultReader, true, "Reader comes from this global")
1041cb0ef41Sopenharmony_ci    const request = otherReader.read();
1051cb0ef41Sopenharmony_ci    assert_equals(request instanceof Promise, true, "Promise still comes from stream's realm (this realm)");
1061cb0ef41Sopenharmony_ci    otherController.close.call(controller);
1071cb0ef41Sopenharmony_ci    assert_equals((await request) instanceof otherGlobal.Object, true, "Object comes from other realm");
1081cb0ef41Sopenharmony_ci}, "Reader objects in correct global");
1091cb0ef41Sopenharmony_ci
1101cb0ef41Sopenharmony_ci
1111cb0ef41Sopenharmony_cipromise_test(async () => {
1121cb0ef41Sopenharmony_ci    const {stream, controller, otherController} = getFreshInstances();
1131cb0ef41Sopenharmony_ci    assert_equals(controller.desiredSize, 1, "Desired size is expected");
1141cb0ef41Sopenharmony_ci    Object.defineProperty(controller, "desiredSize",
1151cb0ef41Sopenharmony_ci        Object.getOwnPropertyDescriptor(OtherReadableStreamDefaultController.prototype, "desiredSize"));
1161cb0ef41Sopenharmony_ci    assert_equals(controller.desiredSize, 1, "Grafting getter from other prototype still returns desired size");
1171cb0ef41Sopenharmony_ci}, "Desired size can be grafted from one prototype to another");
1181cb0ef41Sopenharmony_ci
1191cb0ef41Sopenharmony_cipromise_test(async () => {
1201cb0ef41Sopenharmony_ci    const {stream, controller, otherController} = getFreshInstances();
1211cb0ef41Sopenharmony_ci
1221cb0ef41Sopenharmony_ci    // Make sure the controller close method returns the correct TypeError
1231cb0ef41Sopenharmony_ci    const enqueuedError = { name: "enqueuedError" };
1241cb0ef41Sopenharmony_ci    controller.error(enqueuedError);
1251cb0ef41Sopenharmony_ci
1261cb0ef41Sopenharmony_ci    assert_throws_js(TypeError, () => controller.close(),  "Current Global controller");
1271cb0ef41Sopenharmony_ci    assert_throws_js(otherGlobal.TypeError, () => otherController.close.call(controller),  "Other global controller");
1281cb0ef41Sopenharmony_ci}, "Closing errored stream throws object in appropriate global")
1291cb0ef41Sopenharmony_ci
1301cb0ef41Sopenharmony_cipromise_test(async () => {
1311cb0ef41Sopenharmony_ci    const {otherController} = getFreshInstances();
1321cb0ef41Sopenharmony_ci    // We can enqueue chunks from multiple globals
1331cb0ef41Sopenharmony_ci    const chunk = { name: "chunk" };
1341cb0ef41Sopenharmony_ci
1351cb0ef41Sopenharmony_ci    let controller;
1361cb0ef41Sopenharmony_ci    const stream = new ReadableStream({ start(c) { controller = c; } }, { size() {return 1} });
1371cb0ef41Sopenharmony_ci    otherController.enqueue.call(controller, chunk);
1381cb0ef41Sopenharmony_ci    otherController.enqueue.call(controller, new otherGlobal.Uint8Array(10));
1391cb0ef41Sopenharmony_ci    controller.enqueue(new otherGlobal.Uint8Array(10));
1401cb0ef41Sopenharmony_ci}, "Can enqueue chunks from multiple globals")
1411cb0ef41Sopenharmony_ci
1421cb0ef41Sopenharmony_cipromise_test(async () => {
1431cb0ef41Sopenharmony_ci    const {stream, controller, otherController} = getFreshInstances();
1441cb0ef41Sopenharmony_ci    const chunk = { name: "chunk" };
1451cb0ef41Sopenharmony_ci
1461cb0ef41Sopenharmony_ci    // We get the correct type errors out of a closed stream.
1471cb0ef41Sopenharmony_ci    controller.close();
1481cb0ef41Sopenharmony_ci    assert_throws_js(TypeError, () => controller.enqueue(new otherGlobal.Uint8Array(10)));
1491cb0ef41Sopenharmony_ci    assert_throws_js(otherGlobal.TypeError, () => otherController.enqueue.call(controller, chunk));
1501cb0ef41Sopenharmony_ci    assert_throws_js(otherGlobal.TypeError, () => otherController.enqueue.call(controller, new otherGlobal.Uint8Array(10)));
1511cb0ef41Sopenharmony_ci}, "Correct errors and globals for closed streams");
1521cb0ef41Sopenharmony_ci
1531cb0ef41Sopenharmony_ci
1541cb0ef41Sopenharmony_cipromise_test(async () => {
1551cb0ef41Sopenharmony_ci    const {stream, controller, otherController} = getFreshInstances();
1561cb0ef41Sopenharmony_ci    // Branches out of tee are in the correct global
1571cb0ef41Sopenharmony_ci
1581cb0ef41Sopenharmony_ci    const [branch1, branch2] = otherGlobal.ReadableStream.prototype.tee.call(stream);
1591cb0ef41Sopenharmony_ci    assert_equals(branch1 instanceof ReadableStream, true, "Branch created in this global (as stream is in this global)");
1601cb0ef41Sopenharmony_ci    assert_equals(branch2 instanceof ReadableStream, true, "Branch created in this global (as stream is in this global)");
1611cb0ef41Sopenharmony_ci}, "Tee Branches in correct global");
1621cb0ef41Sopenharmony_ci</script>
163