summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/streams/readable-streams/global.html
blob: 08665d318eac889dfba85abd0efeb0a3168913df (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
<!doctype html>
<meta charset="utf-8">
<title>Ensure Stream objects are created in expected globals. </title>

<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>

<body></body>
<script>
// These tests are loosely derived from Gecko's readable-stream-globals.js,
// which is a test case designed around the JS Streams implementation.
//
// Unlike in JS Streams, where function calls switch realms and change
// the resulting global of the resulting objects, in WebIDL streams,
// the global of an object is (currently underspecified, but) intended
// to be the "Relevant Global" of the 'this' object.
//
// See:
// https://html.spec.whatwg.org/multipage/webappapis.html#relevant
// https://github.com/whatwg/streams/issues/1213
"use strict"

const iframe = document.createElement("iframe")
document.body.append(iframe)

const otherGlobal = iframe.contentWindow;
const OtherReadableStream = otherGlobal.ReadableStream
const OtherReadableStreamDefaultReader = otherGlobal.ReadableStreamDefaultReader;
const OtherReadableStreamDefaultController = otherGlobal.ReadableStreamDefaultController;

promise_test(async () => {

    // Controllers
    let controller;
    let otherController;

    // Get Stream Prototypes and controllers.
    let streamController;
    let stream = new ReadableStream({start(c) { streamController = c; }});

    const callReaderThisGlobal = OtherReadableStream.prototype.getReader.call(stream);
    const newReaderOtherGlobal = new OtherReadableStreamDefaultReader(new ReadableStream());

    // Relevant Global Checking.
    assert_equals(callReaderThisGlobal instanceof ReadableStreamDefaultReader, true, "reader was created in this global (.call)");
    assert_equals(newReaderOtherGlobal instanceof ReadableStreamDefaultReader, false, "reader was created in other global (new)");

    assert_equals(callReaderThisGlobal instanceof OtherReadableStreamDefaultReader, false, "reader isn't coming from other global (.call)" );
    assert_equals(newReaderOtherGlobal instanceof OtherReadableStreamDefaultReader, true, "reader isn't coming from other global (new)");

    assert_equals(otherController instanceof ReadableStreamDefaultController, false, "otherController should come from other gloal")


    const request = callReaderThisGlobal.read();
    assert_equals(request instanceof Promise, true, "Promise comes from this global");

    streamController.close();
    const requestResult = await request;
    assert_equals(requestResult instanceof Object, true, "returned object comes from this global");
}, "Stream objects created in expected globals")

promise_test(async () => {
    const stream = new ReadableStream();
    const otherReader = new OtherReadableStreamDefaultReader(stream);
    const cancelPromise = ReadableStreamDefaultReader.prototype.cancel.call(otherReader);
    assert_equals(cancelPromise instanceof Promise, true, "Cancel promise comes from the same global as the stream");
    assert_equals(await cancelPromise, undefined, "Cancel promise resolves to undefined");
}, "Cancel promise is created in same global as stream")

// Refresh the streams and controllers.
function getFreshInstances() {
    let controller;
    let otherController;
    let stream = new ReadableStream({
        start(c) {
            controller = c;
        }
    });

    new OtherReadableStream({
        start(c) {
            otherController = c;
        }
    });

    return {stream, controller, otherController}
}


promise_test(async () => {
    // Test closed promise on reader from another global (connected to a this-global stream)
    const {stream, controller, otherController} = getFreshInstances();

    const otherReader = new OtherReadableStreamDefaultReader(stream);
    const closedPromise = otherReader.closed;
    assert_equals(closedPromise instanceof otherGlobal.Promise, true, "Closed promise in other global.");
}, "Closed Promise in correct global");

promise_test(async () => {
    const {stream, controller, otherController} = getFreshInstances();

    const otherReader = OtherReadableStream.prototype.getReader.call(stream);
    assert_equals(otherReader instanceof ReadableStreamDefaultReader, true, "Reader comes from this global")
    const request = otherReader.read();
    assert_equals(request instanceof Promise, true, "Promise still comes from stream's realm (this realm)");
    otherController.close.call(controller);
    assert_equals((await request) instanceof otherGlobal.Object, true, "Object comes from other realm");
}, "Reader objects in correct global");


promise_test(async () => {
    const {stream, controller, otherController} = getFreshInstances();
    assert_equals(controller.desiredSize, 1, "Desired size is expected");
    Object.defineProperty(controller, "desiredSize",
        Object.getOwnPropertyDescriptor(OtherReadableStreamDefaultController.prototype, "desiredSize"));
    assert_equals(controller.desiredSize, 1, "Grafting getter from other prototype still returns desired size");
}, "Desired size can be grafted from one prototype to another");

promise_test(async () => {
    const {stream, controller, otherController} = getFreshInstances();

    // Make sure the controller close method returns the correct TypeError
    const enqueuedError = { name: "enqueuedError" };
    controller.error(enqueuedError);

    assert_throws_js(TypeError, () => controller.close(),  "Current Global controller");
    assert_throws_js(otherGlobal.TypeError, () => otherController.close.call(controller),  "Other global controller");
}, "Closing errored stream throws object in appropriate global")

promise_test(async () => {
    const {otherController} = getFreshInstances();
    // We can enqueue chunks from multiple globals
    const chunk = { name: "chunk" };

    let controller;
    const stream = new ReadableStream({ start(c) { controller = c; } }, { size() {return 1} });
    otherController.enqueue.call(controller, chunk);
    otherController.enqueue.call(controller, new otherGlobal.Uint8Array(10));
    controller.enqueue(new otherGlobal.Uint8Array(10));
}, "Can enqueue chunks from multiple globals")

promise_test(async () => {
    const {stream, controller, otherController} = getFreshInstances();
    const chunk = { name: "chunk" };

    // We get the correct type errors out of a closed stream.
    controller.close();
    assert_throws_js(TypeError, () => controller.enqueue(new otherGlobal.Uint8Array(10)));
    assert_throws_js(otherGlobal.TypeError, () => otherController.enqueue.call(controller, chunk));
    assert_throws_js(otherGlobal.TypeError, () => otherController.enqueue.call(controller, new otherGlobal.Uint8Array(10)));
}, "Correct errors and globals for closed streams");


promise_test(async () => {
    const {stream, controller, otherController} = getFreshInstances();
    // Branches out of tee are in the correct global

    const [branch1, branch2] = otherGlobal.ReadableStream.prototype.tee.call(stream);
    assert_equals(branch1 instanceof ReadableStream, true, "Branch created in this global (as stream is in this global)");
    assert_equals(branch2 instanceof ReadableStream, true, "Branch created in this global (as stream is in this global)");
}, "Tee Branches in correct global");
</script>