1<!DOCTYPE html> 2<meta charset="utf-8"> 3<script src="/resources/testharness.js"></script> 4<script src="/resources/testharnessreport.js"></script> 5<script src="resources/helpers.js"></script> 6<script src="../resources/recording-streams.js"></script> 7<script src="../resources/test-utils.js"></script> 8<script> 9'use strict'; 10 11promise_test(async () => { 12 const rs = await createTransferredReadableStream({ 13 start(controller) { 14 controller.enqueue('a'); 15 controller.close(); 16 } 17 }); 18 const reader = rs.getReader(); 19 { 20 const {value, done} = await reader.read(); 21 assert_false(done, 'should not be done yet'); 22 assert_equals(value, 'a', 'first chunk should be a'); 23 } 24 { 25 const {done} = await reader.read(); 26 assert_true(done, 'should be done now'); 27 } 28}, 'sending one chunk through a transferred stream should work'); 29 30promise_test(async () => { 31 let controller; 32 const rs = await createTransferredReadableStream({ 33 start(c) { 34 controller = c; 35 } 36 }); 37 for (let i = 0; i < 10; ++i) { 38 controller.enqueue(i); 39 } 40 controller.close(); 41 const reader = rs.getReader(); 42 for (let i = 0; i < 10; ++i) { 43 const {value, done} = await reader.read(); 44 assert_false(done, 'should not be done yet'); 45 assert_equals(value, i, 'chunk content should match index'); 46 } 47 const {done} = await reader.read(); 48 assert_true(done, 'should be done now'); 49}, 'sending ten chunks through a transferred stream should work'); 50 51promise_test(async () => { 52 let controller; 53 const rs = await createTransferredReadableStream({ 54 start(c) { 55 controller = c; 56 } 57 }); 58 const reader = rs.getReader(); 59 for (let i = 0; i < 10; ++i) { 60 controller.enqueue(i); 61 const {value, done} = await reader.read(); 62 assert_false(done, 'should not be done yet'); 63 assert_equals(value, i, 'chunk content should match index'); 64 } 65 controller.close(); 66 const {done} = await reader.read(); 67 assert_true(done, 'should be done now'); 68}, 'sending ten chunks one at a time should work'); 69 70promise_test(async () => { 71 let controller; 72 const rs = await createTransferredReadableStream({ 73 start() { 74 this.counter = 0; 75 }, 76 pull(controller) { 77 controller.enqueue(this.counter); 78 ++this.counter; 79 if (this.counter === 10) 80 controller.close(); 81 } 82 }); 83 const reader = rs.getReader(); 84 for (let i = 0; i < 10; ++i) { 85 const {value, done} = await reader.read(); 86 assert_false(done, 'should not be done yet'); 87 assert_equals(value, i, 'chunk content should match index'); 88 } 89 const {done} = await reader.read(); 90 assert_true(done, 'should be done now'); 91}, 'sending ten chunks on demand should work'); 92 93promise_test(async () => { 94 const rs = recordingReadableStream({}, { highWaterMark: 0 }); 95 await delay(0); 96 assert_array_equals(rs.events, [], 'pull() should not have been called'); 97 // Eat the message so it can't interfere with other tests. 98 addEventListener('message', () => {}, {once: true}); 99 // The transfer is done manually to verify that it is posting the stream that 100 // relieves backpressure, not receiving it. 101 postMessage(rs, '*', [rs]); 102 await delay(0); 103 assert_array_equals(rs.events, ['pull'], 'pull() should have been called'); 104}, 'transferring a stream should relieve backpressure'); 105 106promise_test(async () => { 107 const rs = await recordingTransferredReadableStream({ 108 pull(controller) { 109 controller.enqueue('a'); 110 } 111 }, { highWaterMark: 2 }); 112 await delay(0); 113 assert_array_equals(rs.events, ['pull', 'pull', 'pull'], 114 'pull() should have been called three times'); 115}, 'transferring a stream should add one chunk to the queue size'); 116 117promise_test(async () => { 118 const rs = await recordingTransferredReadableStream({ 119 start(controller) { 120 controller.enqueue(new Uint8Array(1024)); 121 controller.enqueue(new Uint8Array(1024)); 122 } 123 }, new ByteLengthQueuingStrategy({highWaterMark: 512})); 124 await delay(0); 125 // At this point the queue contains 1024/512 bytes and 1/1 chunk, so it's full 126 // and pull() is not called. 127 assert_array_equals(rs.events, [], 'pull() should not have been called'); 128 const reader = rs.getReader(); 129 const {value, done} = await reader.read(); 130 assert_false(done, 'we should not be done'); 131 assert_equals(value.byteLength, 1024, 'expected chunk should be returned'); 132 // Now the queue contains 0/512 bytes and 1/1 chunk, so pull() is called. If 133 // the implementation erroneously counted the extra queue space in bytes, then 134 // the queue would contain 1024/513 bytes and pull() wouldn't be called. 135 assert_array_equals(rs.events, ['pull'], 'pull() should have been called'); 136}, 'the extra queue from transferring is counted in chunks'); 137 138async function transferredReadableStreamWithCancelPromise() { 139 let resolveCancelCalled; 140 const cancelCalled = new Promise(resolve => { 141 resolveCancelCalled = resolve; 142 }); 143 const rs = await recordingTransferredReadableStream({ 144 cancel() { 145 resolveCancelCalled(); 146 } 147 }); 148 return { rs, cancelCalled }; 149} 150 151promise_test(async () => { 152 const { rs, cancelCalled } = await transferredReadableStreamWithCancelPromise(); 153 rs.cancel('message'); 154 await cancelCalled; 155 assert_array_equals(rs.events, ['pull', 'cancel', 'message'], 156 'cancel() should have been called'); 157 const reader = rs.getReader(); 158 // Check the stream really got closed. 159 await reader.closed; 160}, 'cancel should be propagated to the original'); 161 162promise_test(async () => { 163 const { rs, cancelCalled } = await transferredReadableStreamWithCancelPromise(); 164 const reader = rs.getReader(); 165 const readPromise = reader.read(); 166 reader.cancel('done'); 167 const { done } = await readPromise; 168 assert_true(done, 'should be done'); 169 await cancelCalled; 170 assert_array_equals(rs.events, ['pull', 'cancel', 'done'], 171 'events should match'); 172}, 'cancel should abort a pending read()'); 173 174promise_test(async () => { 175 let cancelComplete = false; 176 const rs = await createTransferredReadableStream({ 177 async cancel() { 178 await flushAsyncEvents(); 179 cancelComplete = true; 180 } 181 }); 182 await rs.cancel(); 183 assert_false(cancelComplete, 184 'cancel() on the underlying sink should not have completed'); 185}, 'stream cancel should not wait for underlying source cancel'); 186 187promise_test(async t => { 188 const rs = await recordingTransferredReadableStream(); 189 const reader = rs.getReader(); 190 let serializationHappened = false; 191 rs.controller.enqueue({ 192 get getter() { 193 serializationHappened = true; 194 return 'a'; 195 } 196 }); 197 await flushAsyncEvents(); 198 assert_false(serializationHappened, 199 'serialization should not have happened yet'); 200 const {value, done} = await reader.read(); 201 assert_false(done, 'should not be done'); 202 assert_equals(value.getter, 'a', 'getter should be a'); 203 assert_true(serializationHappened, 204 'serialization should have happened'); 205}, 'serialization should not happen until the value is read'); 206 207promise_test(async t => { 208 const rs = await recordingTransferredReadableStream(); 209 const reader = rs.getReader(); 210 rs.controller.enqueue(new ReadableStream()); 211 await promise_rejects_dom(t, 'DataCloneError', reader.read(), 212 'closed promise should reject'); 213 assert_throws_js(TypeError, () => rs.controller.enqueue(), 214 'original stream should be errored'); 215}, 'transferring a non-serializable chunk should error both sides'); 216 217promise_test(async t => { 218 const rs = await createTransferredReadableStream({ 219 start(controller) { 220 controller.error('foo'); 221 } 222 }); 223 const reader = rs.getReader(); 224 return promise_rejects_exactly(t, 'foo', reader.read(), 225 'error should be passed through'); 226}, 'errors should be passed through'); 227 228promise_test(async () => { 229 const rs = await recordingTransferredReadableStream(); 230 await delay(0); 231 const reader = rs.getReader(); 232 reader.cancel(); 233 rs.controller.error(); 234 const {done} = await reader.read(); 235 assert_true(done, 'should be done'); 236 assert_throws_js(TypeError, () => rs.controller.enqueue(), 237 'enqueue should throw'); 238}, 'race between cancel() and error() should leave sides in different states'); 239 240promise_test(async () => { 241 const rs = await recordingTransferredReadableStream(); 242 await delay(0); 243 const reader = rs.getReader(); 244 reader.cancel(); 245 rs.controller.close(); 246 const {done} = await reader.read(); 247 assert_true(done, 'should be done'); 248}, 'race between cancel() and close() should be benign'); 249 250promise_test(async () => { 251 const rs = await recordingTransferredReadableStream(); 252 await delay(0); 253 const reader = rs.getReader(); 254 reader.cancel(); 255 rs.controller.enqueue('a'); 256 const {done} = await reader.read(); 257 assert_true(done, 'should be done'); 258}, 'race between cancel() and enqueue() should be benign'); 259 260</script> 261