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/test-utils.js"></script>
7<script src="../resources/recording-streams.js"></script>
8<script>
9'use strict';
10
11promise_test(t => {
12  const orig = new WritableStream();
13  const promise = new Promise(resolve => {
14    addEventListener('message', t.step_func(evt => {
15      const transferred = evt.data;
16      assert_equals(transferred.constructor, WritableStream,
17                    'transferred should be a WritableStream in this realm');
18      assert_true(transferred instanceof WritableStream,
19                  'instanceof check should pass');
20
21      // Perform a brand-check on |transferred|.
22      const writer = WritableStream.prototype.getWriter.call(transferred);
23      resolve();
24    }), {once: true});
25  });
26  postMessage(orig, '*', [orig]);
27  assert_true(orig.locked, 'the original stream should be locked');
28  return promise;
29}, 'window.postMessage should be able to transfer a WritableStream');
30
31test(() => {
32  const ws = new WritableStream();
33  const writer = ws.getWriter();
34  assert_throws_dom('DataCloneError', () => postMessage(ws, '*', [ws]),
35                    'postMessage should throw');
36}, 'a locked WritableStream should not be transferable');
37
38promise_test(t => {
39  const {writable, readable} = new TransformStream();
40  const promise = new Promise(resolve => {
41    addEventListener('message', t.step_func(async evt => {
42      const {writable, readable} = evt.data;
43      const reader = readable.getReader();
44      const writer = writable.getWriter();
45      const writerPromises = Promise.all([
46        writer.write('hi'),
47        writer.close(),
48      ]);
49      const {value, done} = await reader.read();
50      assert_false(done, 'we should not be done');
51      assert_equals(value, 'hi', 'chunk should have been delivered');
52      const readResult = await reader.read();
53      assert_true(readResult.done, 'readable should be closed');
54      await writerPromises;
55      resolve();
56    }), {once: true});
57  });
58  postMessage({writable, readable}, '*', [writable, readable]);
59  return promise;
60}, 'window.postMessage should be able to transfer a {readable, writable} pair');
61
62function transfer(stream) {
63  return new Promise(resolve => {
64    addEventListener('message', evt => resolve(evt.data), { once: true });
65    postMessage(stream, '*', [stream]);
66  });
67}
68
69promise_test(async () => {
70  const orig = new WritableStream(
71    {}, new ByteLengthQueuingStrategy({ highWaterMark: 65536 }));
72  const transferred = await transfer(orig);
73  const writer = transferred.getWriter();
74  assert_equals(writer.desiredSize, 1, 'desiredSize should be 1');
75}, 'desiredSize for a newly-transferred stream should be 1');
76
77promise_test(async () => {
78  const orig = new WritableStream({
79    write() {
80      return new Promise(() => {});
81    }
82  });
83  const transferred = await transfer(orig);
84  const writer = transferred.getWriter();
85  await writer.write('a');
86  assert_equals(writer.desiredSize, 1, 'desiredSize should be 1');
87}, 'effective queue size of a transferred writable should be 2');
88
89promise_test(async () => {
90  const [writeCalled, resolveWriteCalled] = makePromiseAndResolveFunc();
91  let resolveWrite;
92  const orig = new WritableStream({
93    write() {
94      resolveWriteCalled();
95      return new Promise(resolve => {
96        resolveWrite = resolve;
97      });
98    }
99  });
100  const transferred = await transfer(orig);
101  const writer = transferred.getWriter();
102  await writer.write('a');
103  let writeDone = false;
104  const writePromise = writer.write('b').then(() => {
105    writeDone = true;
106  });
107  await writeCalled;
108  assert_false(writeDone, 'second write should not have resolved yet');
109  resolveWrite();
110  await writePromise; // (makes sure this resolves)
111}, 'second write should wait for first underlying write to complete');
112
113async function transferredWritableStreamWithAbortPromise() {
114  const [abortCalled, resolveAbortCalled] = makePromiseAndResolveFunc();
115  const orig = recordingWritableStream({
116    abort() {
117      resolveAbortCalled();
118    }
119  });
120  const transferred = await transfer(orig);
121  return { orig, transferred, abortCalled };
122}
123
124promise_test(async t => {
125  const { orig, transferred, abortCalled } = await transferredWritableStreamWithAbortPromise();
126  transferred.abort('p');
127  await abortCalled;
128  assert_array_equals(orig.events, ['abort', 'p'],
129                      'abort() should have been called');
130}, 'abort() should work');
131
132promise_test(async t => {
133  const { orig, transferred, abortCalled } = await transferredWritableStreamWithAbortPromise();
134  const writer = transferred.getWriter();
135  // A WritableStream object cannot be cloned.
136  await promise_rejects_dom(t, 'DataCloneError', writer.write(new WritableStream()),
137                            'the write should reject');
138  await promise_rejects_dom(t, 'DataCloneError', writer.closed,
139                            'the stream should be errored');
140  await abortCalled;
141  assert_equals(orig.events.length, 2, 'abort should have been called');
142  assert_equals(orig.events[0], 'abort', 'first event should be abort');
143  assert_equals(orig.events[1].name, 'DataCloneError',
144                'reason should be a DataCloneError');
145}, 'writing a unclonable object should error the stream');
146</script>
147