blob: 3a4c0f56d656fc88485a90467adfd2c56a9a142a [file] [log] [blame]
Soares Chen17354db2017-06-13 18:39:041<!doctype html>
2<meta charset=utf-8>
Byron Campen [:bwc]734e5ae2019-04-25 00:24:563<meta name="timeout" content="long">
Soares Chen17354db2017-06-13 18:39:044<title>RTCDataChannel.prototype.send</title>
5<script src="/resources/testharness.js"></script>
6<script src="/resources/testharnessreport.js"></script>
7<script src="RTCPeerConnection-helper.js"></script>
8<script>
9 'use strict';
10
11 // Test is based on the following editor draft:
12 // https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.html
13
14 // The following helper functions are called from RTCPeerConnection-helper.js:
15 // createDataChannelPair
16 // awaitMessage
17 // blobToArrayBuffer
youennfb75b8762018-12-20 21:29:2518 // assert_equals_typed_array
Soares Chen17354db2017-06-13 18:39:0419
20 /*
21 6.2. RTCDataChannel
22 interface RTCDataChannel : EventTarget {
23 ...
24 readonly attribute RTCDataChannelState readyState;
25 readonly attribute unsigned long bufferedAmount;
26 attribute EventHandler onmessage;
27 attribute DOMString binaryType;
28
29 void send(USVString data);
30 void send(Blob data);
31 void send(ArrayBuffer data);
32 void send(ArrayBufferView data);
33 };
34 */
35
36 // Simple ASCII encoded string
37 const helloString = 'hello';
38 // ASCII encoded buffer representation of the string
39 const helloBuffer = Uint8Array.of(0x68, 0x65, 0x6c, 0x6c, 0x6f);
40 const helloBlob = new Blob([helloBuffer]);
41
42 // Unicode string with multiple code units
43 const unicodeString = '世界你好';
44 // UTF-8 encoded buffer representation of the string
45 const unicodeBuffer = Uint8Array.of(
46 0xe4, 0xb8, 0x96, 0xe7, 0x95, 0x8c,
47 0xe4, 0xbd, 0xa0, 0xe5, 0xa5, 0xbd);
48
49 /*
50 6.2. send()
51 2. If channel's readyState attribute is connecting, throw an InvalidStateError.
52 */
53 test(t => {
54 const pc = new RTCPeerConnection();
55 const channel = pc.createDataChannel('test');
56 assert_equals(channel.readyState, 'connecting');
Stephen McGruerd5103042020-01-23 21:45:4557 assert_throws_dom('InvalidStateError', () => channel.send(helloString));
Soares Chen17354db2017-06-13 18:39:0458 }, 'Calling send() when data channel is in connecting state should throw InvalidStateError');
59
60 /*
61 6.2. send()
62 3. Execute the sub step that corresponds to the type of the methods argument:
63
64 string object
65 Let data be the object and increase the bufferedAmount attribute
66 by the number of bytes needed to express data as UTF-8.
67
68 [WebSocket]
69 5. Feedback from the protocol
70 When a WebSocket message has been received
71 4. If type indicates that the data is Text, then initialize event's data
72 attribute to data.
73 */
74 promise_test(t => {
75 return createDataChannelPair()
76 .then(([channel1, channel2]) => {
77 channel1.send(helloString);
78 return awaitMessage(channel2)
79 }).then(message => {
80 assert_equals(typeof message, 'string',
81 'Expect message to be a string');
82
83 assert_equals(message, helloString);
84 });
85 }, 'Data channel should be able to send simple string and receive as string');
86
87 promise_test(t => {
88 return createDataChannelPair()
89 .then(([channel1, channel2]) => {
90 channel1.send(unicodeString);
91 return awaitMessage(channel2)
92 }).then(message => {
93 assert_equals(typeof message, 'string',
94 'Expect message to be a string');
95
96 assert_equals(message, unicodeString);
97 });
98 }, 'Data channel should be able to send unicode string and receive as unicode string');
99 promise_test(t => {
100 return createDataChannelPair()
101 .then(([channel1, channel2]) => {
102 channel2.binaryType = 'arraybuffer';
103 channel1.send(helloString);
104 return awaitMessage(channel2);
105 }).then(message => {
106 assert_equals(typeof message, 'string',
107 'Expect message to be a string');
108
109 assert_equals(message, helloString);
110 });
111 }, 'Data channel should ignore binaryType and always receive string message as string');
112
113 /*
114 6.2. send()
115 3. Execute the sub step that corresponds to the type of the methods argument:
116 ArrayBufferView object
117 Let data be the data stored in the section of the buffer described
118 by the ArrayBuffer object that the ArrayBufferView object references
119 and increase the bufferedAmount attribute by the length of the
120 ArrayBufferView in bytes.
121
122 [WebSocket]
123 5. Feedback from the protocol
124 When a WebSocket message has been received
125 4. If binaryType is set to "arraybuffer", then initialize event's data
126 attribute to a new read-only ArrayBuffer object whose contents are data.
127
128 [WebIDL]
129 4.1. ArrayBufferView
130 typedef (Int8Array or Int16Array or Int32Array or
131 Uint8Array or Uint16Array or Uint32Array or Uint8ClampedArray or
132 Float32Array or Float64Array or DataView) ArrayBufferView;
133 */
134 promise_test(t => {
135 return createDataChannelPair()
136 .then(([channel1, channel2]) => {
137 channel2.binaryType = 'arraybuffer';
138 channel1.send(helloBuffer);
139 return awaitMessage(channel2)
140 }).then(messageBuffer => {
141 assert_true(messageBuffer instanceof ArrayBuffer,
142 'Expect messageBuffer to be an ArrayBuffer');
143
youennfb75b8762018-12-20 21:29:25144 assert_equals_typed_array(messageBuffer, helloBuffer.buffer);
Soares Chen17354db2017-06-13 18:39:04145 });
146 }, 'Data channel should be able to send Uint8Array message and receive as ArrayBuffer');
147
148 /*
149 6.2. send()
150 3. Execute the sub step that corresponds to the type of the methods argument:
151 ArrayBuffer object
152 Let data be the data stored in the buffer described by the ArrayBuffer
153 object and increase the bufferedAmount attribute by the length of the
154 ArrayBuffer in bytes.
155 */
156 promise_test(t => {
157 return createDataChannelPair()
158 .then(([channel1, channel2]) => {
159 channel2.binaryType = 'arraybuffer';
160 channel1.send(helloBuffer.buffer);
161 return awaitMessage(channel2)
162 }).then(messageBuffer => {
163 assert_true(messageBuffer instanceof ArrayBuffer,
164 'Expect messageBuffer to be an ArrayBuffer');
165
youennfb75b8762018-12-20 21:29:25166 assert_equals_typed_array(messageBuffer, helloBuffer.buffer);
Soares Chen17354db2017-06-13 18:39:04167 });
168 }, 'Data channel should be able to send ArrayBuffer message and receive as ArrayBuffer');
169
170 /*
171 6.2. send()
172 3. Execute the sub step that corresponds to the type of the methods argument:
173 Blob object
174 Let data be the raw data represented by the Blob object and increase
175 the bufferedAmount attribute by the size of data, in bytes.
176 */
177 promise_test(t => {
178 return createDataChannelPair()
179 .then(([channel1, channel2]) => {
180 channel2.binaryType = 'arraybuffer';
181 channel1.send(helloBlob);
182 return awaitMessage(channel2);
183 }).then(messageBuffer => {
184 assert_true(messageBuffer instanceof ArrayBuffer,
185 'Expect messageBuffer to be an ArrayBuffer');
186
youennfb75b8762018-12-20 21:29:25187 assert_equals_typed_array(messageBuffer, helloBuffer.buffer);
Soares Chen17354db2017-06-13 18:39:04188 });
189 }, 'Data channel should be able to send Blob message and receive as ArrayBuffer');
190
191 /*
192 [WebSocket]
193 5. Feedback from the protocol
194 When a WebSocket message has been received
195 4. If binaryType is set to "blob", then initialize event's data attribute
196 to a new Blob object that represents data as its raw data.
197 */
198 promise_test(t => {
199 return createDataChannelPair()
200 .then(([channel1, channel2]) => {
201 channel2.binaryType = 'blob';
202 channel1.send(helloBuffer);
203 return awaitMessage(channel2);
204 })
205 .then(messageBlob => {
206 assert_true(messageBlob instanceof Blob,
207 'Expect received messageBlob to be a Blob');
208
209 return blobToArrayBuffer(messageBlob);
210 }).then(messageBuffer => {
211 assert_true(messageBuffer instanceof ArrayBuffer,
212 'Expect messageBuffer to be an ArrayBuffer');
213
youennfb75b8762018-12-20 21:29:25214 assert_equals_typed_array(messageBuffer, helloBuffer.buffer);
Soares Chen17354db2017-06-13 18:39:04215 });
216 }, 'Data channel should be able to send ArrayBuffer message and receive as Blob');
217
218 /*
219 6.2. RTCDataChannel
220 binaryType
221 The binaryType attribute must, on getting, return the value to which it was
222 last set. On setting, the user agent must set the IDL attribute to the new
223 value. When a RTCDataChannel object is created, the binaryType attribute must
224 be initialized to the string "blob".
225 */
226 promise_test(t => {
227 return createDataChannelPair()
228 .then(([channel1, channel2]) => {
229 assert_equals(channel2.binaryType, 'blob',
230 'Expect initial binaryType value to be blob');
231
232 channel1.send(helloBuffer);
233 return awaitMessage(channel2);
234 })
235 .then(messageBlob => {
236 assert_true(messageBlob instanceof Blob,
237 'Expect received messageBlob to be a Blob');
238
239 return blobToArrayBuffer(messageBlob);
240 }).then(messageBuffer => {
241 assert_true(messageBuffer instanceof ArrayBuffer,
242 'Expect messageBuffer to be an ArrayBuffer');
243
youennfb75b8762018-12-20 21:29:25244 assert_equals_typed_array(messageBuffer, helloBuffer.buffer);
Soares Chen17354db2017-06-13 18:39:04245 });
246 }, 'Data channel binaryType should receive message as Blob by default');
247
248 // Test sending 3 messages: helloBuffer, unicodeString, helloBlob
249 async_test(t => {
250 const receivedMessages = [];
251
252 const onMessage = t.step_func(event => {
253 const { data } = event;
254 receivedMessages.push(data);
255
256 if(receivedMessages.length === 3) {
youennfb75b8762018-12-20 21:29:25257 assert_equals_typed_array(receivedMessages[0], helloBuffer.buffer);
Soares Chen17354db2017-06-13 18:39:04258 assert_equals(receivedMessages[1], unicodeString);
youennfb75b8762018-12-20 21:29:25259 assert_equals_typed_array(receivedMessages[2], helloBuffer.buffer);
Soares Chen17354db2017-06-13 18:39:04260
261 t.done();
262 }
263 });
264
265 createDataChannelPair()
266 .then(([channel1, channel2]) => {
267 channel2.binaryType = 'arraybuffer';
268 channel2.addEventListener('message', onMessage);
269
270 channel1.send(helloBuffer);
271 channel1.send(unicodeString);
272 channel1.send(helloBlob);
273
274 }).catch(t.step_func(err =>
275 assert_unreached(`Unexpected promise rejection: ${err}`)));
276 }, 'Sending multiple messages with different types should succeed and be received');
277
278 /*
279 [Deferred]
280 6.2. RTCDataChannel
281 The send() method is being amended in w3c/webrtc-pc#1209 to throw error instead
282 of closing data channel when buffer is full
283
284 send()
285 4. If channel's underlying data transport is not established yet, or if the
286 closing procedure has started, then abort these steps.
287 5. Attempt to send data on channel's underlying data transport; if the data
288 cannot be sent, e.g. because it would need to be buffered but the buffer
289 is full, the user agent must abruptly close channel's underlying data
290 transport with an error.
291
292 test(t => {
293 const pc = new RTCPeerConnection();
294 const channel = pc.createDataChannel('test');
295 channel.close();
296 assert_equals(channel.readyState, 'closing');
297 channel.send(helloString);
298 }, 'Calling send() when data channel is in closing state should succeed');
Harald Alvestrand41456512019-04-10 13:12:48299 */
300
301promise_test(async t => {
302 let pc1 = new RTCPeerConnection();
Philipp Hanckefb60c652019-05-20 07:39:18303 t.add_cleanup(() => pc1.close());
Harald Alvestrand41456512019-04-10 13:12:48304 let [channel1, channel2] = await createDataChannelPair(pc1);
305 let message = 'hello888'; // 8 bytes
306 while (message.length <= pc1.sctp.maxMessageSize) {
307 channel1.send(message);
308 let received_message = await awaitMessage(channel2);
309 assert_equals(received_message.length, message.length, "Size mismatch");
310 // Double size
311 message = message + message;
312 }
313 // "send" method step 4:
314 // If the byte size of "data" exceeds the value of maxMessageSize, throw
315 // a TypeError.
Boris Zbarsky90b42a52020-02-18 22:40:43316 assert_throws_js(TypeError, () => channel1.send(message));
Harald Alvestrand41456512019-04-10 13:12:48317}, 'Calling send() up to max size should succeed, above max size should fail');
Soares Chen17354db2017-06-13 18:39:04318</script>