blob: 8a13155358de3dab22a77f50172aae5bf0789b43 [file] [log] [blame]
foolip72525e92017-04-24 09:31:031<!doctype html>
2<meta charset=utf-8>
Soares Chen6a9b7812017-06-08 13:25:473<title>RTCPeerConnection.prototype.createDataChannel</title>
foolip72525e92017-04-24 09:31:034<script src=/resources/testharness.js></script>
5<script src=/resources/testharnessreport.js></script>
youennf0e97d092019-03-23 18:02:566<script src="RTCPeerConnection-helper.js"></script>
foolip72525e92017-04-24 09:31:037<script>
8'use strict';
9
youennf0e97d092019-03-23 18:02:5610// Test is based on the following revision:
11// https://rawgit.com/w3c/webrtc-pc/1cc5bfc3ff18741033d804c4a71f7891242fb5b3/webrtc.html
Soares Chen6a9b7812017-06-08 13:25:4712
13/*
14 6.1. RTCPeerConnection Interface Extensions
15
16 partial interface RTCPeerConnection {
youennf0e97d092019-03-23 18:02:5617 [...]
18 RTCDataChannel createDataChannel(USVString label,
19 optional RTCDataChannelInit dataChannelDict);
20 [...]
Soares Chen6a9b7812017-06-08 13:25:4721 };
22
23 6.2. RTCDataChannel
Soares Chen199b65f2017-06-21 09:20:3924
25 interface RTCDataChannel : EventTarget {
youennf0e97d092019-03-23 18:02:5626 readonly attribute USVString label;
27 readonly attribute boolean ordered;
28 readonly attribute unsigned short? maxPacketLifeTime;
29 readonly attribute unsigned short? maxRetransmits;
30 readonly attribute USVString protocol;
31 readonly attribute boolean negotiated;
32 readonly attribute unsigned short? id;
33 readonly attribute RTCPriorityType priority;
34 readonly attribute RTCDataChannelState readyState;
35 readonly attribute unsigned long bufferedAmount;
36 attribute unsigned long bufferedAmountLowThreshold;
37 [...]
38 attribute DOMString binaryType;
39 [...]
Soares Chen199b65f2017-06-21 09:20:3940 };
41
Soares Chen6a9b7812017-06-08 13:25:4742 dictionary RTCDataChannelInit {
youennf0e97d092019-03-23 18:02:5643 boolean ordered = true;
44 unsigned short maxPacketLifeTime;
45 unsigned short maxRetransmits;
46 USVString protocol = "";
47 boolean negotiated = false;
48 [EnforceRange]
49 unsigned short id;
50 RTCPriorityType priority = "low";
Soares Chen6a9b7812017-06-08 13:25:4751 };
Soares Chen199b65f2017-06-21 09:20:3952
53 4.9.1. RTCPriorityType Enum
54
55 enum RTCPriorityType {
56 "very-low",
57 "low",
58 "medium",
59 "high"
60 };
Soares Chen6a9b7812017-06-08 13:25:4761 */
foolip72525e92017-04-24 09:31:0362
Harald Alvestrandf6cf3bd2019-04-04 12:39:5763test(t => {
Soares Chen6a9b7812017-06-08 13:25:4764 const pc = new RTCPeerConnection();
youennf0e97d092019-03-23 18:02:5665 t.add_cleanup(() => pc.close());
66
Soares Chen6a9b7812017-06-08 13:25:4767 assert_equals(pc.createDataChannel.length, 1);
68 assert_throws(new TypeError(), () => pc.createDataChannel());
69}, 'createDataChannel with no argument should throw TypeError');
70
71/*
72 6.2. createDataChannel
73 2. If connection's [[isClosed]] slot is true, throw an InvalidStateError.
74 */
Harald Alvestrandf6cf3bd2019-04-04 12:39:5775test(t => {
Soares Chen6a9b7812017-06-08 13:25:4776 const pc = new RTCPeerConnection();
foolip72525e92017-04-24 09:31:0377 pc.close();
78 assert_equals(pc.signalingState, 'closed', 'signaling state');
79 assert_throws('InvalidStateError', () => pc.createDataChannel(''));
Soares Chen6a9b7812017-06-08 13:25:4780}, 'createDataChannel with closed connection should throw InvalidStateError');
foolip72525e92017-04-24 09:31:0381
Soares Chen6a9b7812017-06-08 13:25:4782/*
Soares Chen199b65f2017-06-21 09:20:3983 6.1. createDataChannel
youennf0e97d092019-03-23 18:02:5684 4. Let channel have a [[DataChannelLabel]] internal slot initialized to the value of the
Soares Chen199b65f2017-06-21 09:20:3985 first argument.
youennf0e97d092019-03-23 18:02:5686 6. Let options be the second argument.
87 7. Let channel have an [[MaxPacketLifeTime]] internal slot initialized to
Soares Chen199b65f2017-06-21 09:20:3988 option's maxPacketLifeTime member, if present, otherwise null.
youennf0e97d092019-03-23 18:02:5689 8. Let channel have a [[ReadyState]] internal slot initialized to "connecting".
90 9. Let channel have a [[BufferedAmount]] internal slot initialized to 0.
91 10. Let channel have an [[MaxRetransmits]] internal slot initialized to
Soares Chen199b65f2017-06-21 09:20:3992 option's maxRetransmits member, if present, otherwise null.
youennf0e97d092019-03-23 18:02:5693 11. Let channel have an [[Ordered]] internal slot initialized to option's
Soares Chen199b65f2017-06-21 09:20:3994 ordered member.
youennf0e97d092019-03-23 18:02:5695 12. Let channel have a [[DataChannelProtocol]] internal slot initialized to option's
Soares Chen199b65f2017-06-21 09:20:3996 protocol member.
youennf0e97d092019-03-23 18:02:5697 14. Let channel have a [[Negotiated]] internal slot initialized to option's negotiated
98 member.
99 15. Let channel have an [[DataChannelId]] internal slot initialized to option's id
100 member, if it is present and [[Negotiated]] is true, otherwise null.
101 17. Let channel have an [[DataChannelPriority]] internal slot initialized to option's
102 priority member.
103 21. If the [[DataChannelId]] slot is null (due to no ID being passed into
104 createDataChannel, or [[Negotiated]] being false), and the DTLS role of the SCTP
105 transport has already been negotiated, then initialize [[DataChannelId]] to a value
106 generated by the user agent, according to [RTCWEB-DATA-PROTOCOL], and skip
107 to the next step. If no available ID could be generated, or if the value of the
108 [[DataChannelId]] slot is being used by an existing RTCDataChannel, throw an
109 OperationError exception.
110
111 Note
112 If the [[DataChannelId]] slot is null after this step, it will be populated once
113 the DTLS role is determined during the process of setting an RTCSessionDescription.
114 22. If channel is the first RTCDataChannel created on connection, update the
115 negotiation-needed flag for connection.
116
Soares Chen199b65f2017-06-21 09:20:39117
118 6.2. RTCDataChannel
119
120 A RTCDataChannel, created with createDataChannel or dispatched via a
121 RTCDataChannelEvent, MUST initially be in the connecting state
122
youennf0e97d092019-03-23 18:02:56123 bufferedAmountLowThreshold
124 [...] The bufferedAmountLowThreshold is initially zero on each new RTCDataChannel,
125 but the application may change its value at any time.
126
Soares Chen199b65f2017-06-21 09:20:39127 binaryType
youennf0e97d092019-03-23 18:02:56128 [...] When a RTCDataChannel object is created, the binaryType attribute MUST
Soares Chen199b65f2017-06-21 09:20:39129 be initialized to the string "blob".
Soares Chen6a9b7812017-06-08 13:25:47130 */
Harald Alvestrandf6cf3bd2019-04-04 12:39:57131test(t => {
Soares Chen6a9b7812017-06-08 13:25:47132 const pc = new RTCPeerConnection();
youennf0e97d092019-03-23 18:02:56133 t.add_cleanup(() => pc.close());
Soares Chen199b65f2017-06-21 09:20:39134
youennf0e97d092019-03-23 18:02:56135 const dc = pc.createDataChannel('');
136
137 assert_true(dc instanceof RTCDataChannel, 'is RTCDataChannel');
138 assert_equals(dc.label, '');
139 assert_equals(dc.ordered, true);
140 assert_equals(dc.maxPacketLifeTime, null);
141 assert_equals(dc.maxRetransmits, null);
142 assert_equals(dc.protocol, '');
143 assert_equals(dc.negotiated, false);
Taylor Brandstetterce56f0e2017-05-09 13:53:14144 // Since no offer/answer exchange has occurred yet, the DTLS role is unknown
145 // and so the ID should be null.
youennf0e97d092019-03-23 18:02:56146 assert_equals(dc.id, null);
147 assert_equals(dc.priority, 'low');
148 assert_equals(dc.readyState, 'connecting');
149 assert_equals(dc.bufferedAmount, 0);
150 assert_equals(dc.bufferedAmountLowThreshold, 0);
151 assert_equals(dc.binaryType, 'blob');
Soares Chen6a9b7812017-06-08 13:25:47152}, 'createDataChannel attribute default values');
foolip72525e92017-04-24 09:31:03153
Harald Alvestrandf6cf3bd2019-04-04 12:39:57154test(t => {
Soares Chen199b65f2017-06-21 09:20:39155 const pc = new RTCPeerConnection();
youennf0e97d092019-03-23 18:02:56156 t.add_cleanup(() => pc.close());
157
158 const dc = pc.createDataChannel('test', {
Soares Chen199b65f2017-06-21 09:20:39159 ordered: false,
Soares Chen199b65f2017-06-21 09:20:39160 maxRetransmits: 1,
Harald Alvestrandf6cf3bd2019-04-04 12:39:57161 // Note: maxPacketLifeTime is not set in this test.
Soares Chen199b65f2017-06-21 09:20:39162 protocol: 'custom',
163 negotiated: true,
164 id: 3,
165 priority: 'high'
166 });
167
youennf0e97d092019-03-23 18:02:56168 assert_true(dc instanceof RTCDataChannel, 'is RTCDataChannel');
169 assert_equals(dc.label, 'test');
170 assert_equals(dc.ordered, false);
171 assert_equals(dc.maxPacketLifeTime, null);
172 assert_equals(dc.maxRetransmits, 1);
173 assert_equals(dc.protocol, 'custom');
174 assert_equals(dc.negotiated, true);
175 assert_equals(dc.id, 3);
176 assert_equals(dc.priority, 'high');
177 assert_equals(dc.readyState, 'connecting');
178 assert_equals(dc.bufferedAmount, 0);
179 assert_equals(dc.bufferedAmountLowThreshold, 0);
180 assert_equals(dc.binaryType, 'blob');
Harald Alvestrandf6cf3bd2019-04-04 12:39:57181
182 const dc2 = pc.createDataChannel('test2', {
183 ordered: false,
184 maxPacketLifeTime: 42
185 });
186 assert_equals(dc2.label, 'test2');
187 assert_equals(dc2.maxPacketLifeTime, 42);
188 assert_equals(dc.maxRetransmits, null);
Soares Chen199b65f2017-06-21 09:20:39189}, 'createDataChannel with provided parameters should initialize attributes to provided values');
Soares Chen6a9b7812017-06-08 13:25:47190
191/*
192 6.2. createDataChannel
youennf0e97d092019-03-23 18:02:56193 4. Let channel have a [[DataChannelLabel]] internal slot initialized to the value of the
Soares Chen199b65f2017-06-21 09:20:39194 first argument.
Soares Chen6a9b7812017-06-08 13:25:47195
196 [ECMA262] 7.1.12. ToString(argument)
197 undefined -> "undefined"
198 null -> "null"
199
200 [WebIDL] 3.10.15. Convert a DOMString to a sequence of Unicode scalar values
201 */
foolip72525e92017-04-24 09:31:03202const labels = [
203 ['"foo"', 'foo', 'foo'],
204 ['null', null, 'null'],
205 ['undefined', undefined, 'undefined'],
206 ['lone surrogate', '\uD800', '\uFFFD'],
207];
208for (const [description, label, expected] of labels) {
Harald Alvestrandf6cf3bd2019-04-04 12:39:57209 test(t => {
foolip72525e92017-04-24 09:31:03210 const pc = new RTCPeerConnection;
youennf0e97d092019-03-23 18:02:56211 t.add_cleanup(() => pc.close());
212
213 const dc = pc.createDataChannel(label);
214 assert_equals(dc.label, expected);
Soares Chen6a9b7812017-06-08 13:25:47215 }, `createDataChannel with label ${description} should succeed`);
foolip72525e92017-04-24 09:31:03216}
217
Soares Chen6a9b7812017-06-08 13:25:47218/*
219 6.2. RTCDataChannel
Soares Chen199b65f2017-06-21 09:20:39220 createDataChannel
youennf0e97d092019-03-23 18:02:56221 11. Let channel have an [[Ordered]] internal slot initialized to option's
Soares Chen199b65f2017-06-21 09:20:39222 ordered member.
Soares Chen6a9b7812017-06-08 13:25:47223 */
Harald Alvestrandf6cf3bd2019-04-04 12:39:57224test(t => {
Soares Chen6a9b7812017-06-08 13:25:47225 const pc = new RTCPeerConnection();
youennf0e97d092019-03-23 18:02:56226 t.add_cleanup(() => pc.close());
227
228 const dc = pc.createDataChannel('', { ordered: false });
229 assert_equals(dc.ordered, false);
Soares Chen6a9b7812017-06-08 13:25:47230}, 'createDataChannel with ordered false should succeed');
foolip72525e92017-04-24 09:31:03231
232// true as the default value of a boolean is confusing because null is converted
233// to false while undefined is converted to true.
Harald Alvestrandf6cf3bd2019-04-04 12:39:57234test(t => {
Soares Chen6a9b7812017-06-08 13:25:47235 const pc = new RTCPeerConnection();
youennf0e97d092019-03-23 18:02:56236 t.add_cleanup(() => pc.close());
237
238 const dc1 = pc.createDataChannel('', { ordered: null });
239 assert_equals(dc1.ordered, false);
240 const dc2 = pc.createDataChannel('', { ordered: undefined });
241 assert_equals(dc2.ordered, true);
Soares Chen6a9b7812017-06-08 13:25:47242}, 'createDataChannel with ordered null/undefined should succeed');
foolip72525e92017-04-24 09:31:03243
Soares Chen6a9b7812017-06-08 13:25:47244/*
245 6.2. RTCDataChannel
Soares Chen199b65f2017-06-21 09:20:39246 createDataChannel
youennf0e97d092019-03-23 18:02:56247 7. Let channel have an [[MaxPacketLifeTime]] internal slot initialized to
Soares Chen199b65f2017-06-21 09:20:39248 option's maxPacketLifeTime member, if present, otherwise null.
Soares Chen6a9b7812017-06-08 13:25:47249 */
Harald Alvestrandf6cf3bd2019-04-04 12:39:57250test(t => {
foolip72525e92017-04-24 09:31:03251 const pc = new RTCPeerConnection;
youennf0e97d092019-03-23 18:02:56252 t.add_cleanup(() => pc.close());
253
254 const dc = pc.createDataChannel('', { maxPacketLifeTime: 0 });
255 assert_equals(dc.maxPacketLifeTime, 0);
Soares Chen6a9b7812017-06-08 13:25:47256}, 'createDataChannel with maxPacketLifeTime 0 should succeed');
foolip72525e92017-04-24 09:31:03257
Soares Chen6a9b7812017-06-08 13:25:47258/*
259 6.2. RTCDataChannel
Soares Chen199b65f2017-06-21 09:20:39260 createDataChannel
youennf0e97d092019-03-23 18:02:56261 10. Let channel have an [[MaxRetransmits]] internal slot initialized to
Soares Chen199b65f2017-06-21 09:20:39262 option's maxRetransmits member, if present, otherwise null.
Soares Chen6a9b7812017-06-08 13:25:47263 */
Harald Alvestrandf6cf3bd2019-04-04 12:39:57264test(t => {
foolip72525e92017-04-24 09:31:03265 const pc = new RTCPeerConnection;
youennf0e97d092019-03-23 18:02:56266 t.add_cleanup(() => pc.close());
267
268 const dc = pc.createDataChannel('', { maxRetransmits: 0 });
269 assert_equals(dc.maxRetransmits, 0);
Soares Chen6a9b7812017-06-08 13:25:47270}, 'createDataChannel with maxRetransmits 0 should succeed');
foolip72525e92017-04-24 09:31:03271
Soares Chen6a9b7812017-06-08 13:25:47272/*
273 6.2. createDataChannel
youennf0e97d092019-03-23 18:02:56274 18. If both [[MaxPacketLifeTime]] and [[MaxRetransmits]] attributes are set (not null),
275 throw a TypeError.
Soares Chen6a9b7812017-06-08 13:25:47276 */
Harald Alvestrandf6cf3bd2019-04-04 12:39:57277test(t => {
foolip72525e92017-04-24 09:31:03278 const pc = new RTCPeerConnection;
youennf0e97d092019-03-23 18:02:56279 t.add_cleanup(() => pc.close());
280
281 pc.createDataChannel('', {
Harald Alvestrandf6cf3bd2019-04-04 12:39:57282 maxPacketLifeTime: undefined,
283 maxRetransmits: undefined
youennf0e97d092019-03-23 18:02:56284 });
Harald Alvestrandf6cf3bd2019-04-04 12:39:57285}, 'createDataChannel with both maxPacketLifeTime and maxRetransmits undefined should succeed');
youennf0e97d092019-03-23 18:02:56286
Harald Alvestrandf6cf3bd2019-04-04 12:39:57287test(t => {
youennf0e97d092019-03-23 18:02:56288 const pc = new RTCPeerConnection;
289 t.add_cleanup(() => pc.close());
290
Soares Chen199b65f2017-06-21 09:20:39291 assert_throws(new TypeError(), () => pc.createDataChannel('', {
foolip72525e92017-04-24 09:31:03292 maxPacketLifeTime: 0,
293 maxRetransmits: 0
294 }));
youennf0e97d092019-03-23 18:02:56295 assert_throws(new TypeError(), () => pc.createDataChannel('', {
296 maxPacketLifeTime: 42,
297 maxRetransmits: 42
298 }));
299}, 'createDataChannel with both maxPacketLifeTime and maxRetransmits should throw TypeError');
foolip72525e92017-04-24 09:31:03300
Soares Chen6a9b7812017-06-08 13:25:47301/*
302 6.2. RTCDataChannel
Soares Chen199b65f2017-06-21 09:20:39303 createDataChannel
youennf0e97d092019-03-23 18:02:56304 12. Let channel have a [[DataChannelProtocol]] internal slot initialized to option's
Soares Chen199b65f2017-06-21 09:20:39305 protocol member.
Soares Chen6a9b7812017-06-08 13:25:47306 */
foolip72525e92017-04-24 09:31:03307const protocols = [
308 ['"foo"', 'foo', 'foo'],
309 ['null', null, 'null'],
310 ['undefined', undefined, ''],
311 ['lone surrogate', '\uD800', '\uFFFD'],
312];
313for (const [description, protocol, expected] of protocols) {
Harald Alvestrandf6cf3bd2019-04-04 12:39:57314 test(t => {
foolip72525e92017-04-24 09:31:03315 const pc = new RTCPeerConnection;
youennf0e97d092019-03-23 18:02:56316 t.add_cleanup(() => pc.close());
317
318 const dc = pc.createDataChannel('', { protocol });
319 assert_equals(dc.protocol, expected);
Soares Chen6a9b7812017-06-08 13:25:47320 }, `createDataChannel with protocol ${description} should succeed`);
foolip72525e92017-04-24 09:31:03321}
322
Soares Chen6a9b7812017-06-08 13:25:47323/*
324 6.2. RTCDataChannel
Soares Chen199b65f2017-06-21 09:20:39325 createDataChannel
youennf0e97d092019-03-23 18:02:56326 20. If [[DataChannelId]] is equal to 65535, which is greater than the maximum allowed
327 ID of 65534 but still qualifies as an unsigned short, throw a TypeError.
Soares Chen6a9b7812017-06-08 13:25:47328 */
329for (const id of [0, 1, 65534]) {
Harald Alvestrandf6cf3bd2019-04-04 12:39:57330 test(t => {
Soares Chen6a9b7812017-06-08 13:25:47331 const pc = new RTCPeerConnection();
youennf0e97d092019-03-23 18:02:56332 t.add_cleanup(() => pc.close());
333
Harald Alvestrand267d5dd2019-04-02 12:40:16334 const dc = pc.createDataChannel('', { 'negotiated': true, 'id': id });
youennf0e97d092019-03-23 18:02:56335 assert_equals(dc.id, id);
Soares Chen6a9b7812017-06-08 13:25:47336 }, `createDataChannel with id ${id} should succeed`);
foolip72525e92017-04-24 09:31:03337}
338
Soares Chen6a9b7812017-06-08 13:25:47339for (const id of [-1, 65535, 65536]) {
Harald Alvestrandf6cf3bd2019-04-04 12:39:57340 test(t => {
Soares Chen6a9b7812017-06-08 13:25:47341 const pc = new RTCPeerConnection();
youennf0e97d092019-03-23 18:02:56342 t.add_cleanup(() => pc.close());
343
Harald Alvestrand267d5dd2019-04-02 12:40:16344 assert_throws(new TypeError(), () => pc.createDataChannel('',
345 { 'negotiated': true, 'id': id }));
Soares Chen6a9b7812017-06-08 13:25:47346 }, `createDataChannel with id ${id} should throw TypeError`);
347}
348
349/*
350 6.2. RTCDataChannel
Soares Chen199b65f2017-06-21 09:20:39351 createDataChannel
youennf0e97d092019-03-23 18:02:56352 17. Let channel have an [[DataChannelPriority]] internal slot initialized to option's
353 priority member.
Soares Chen6a9b7812017-06-08 13:25:47354 */
Harald Alvestrandf6cf3bd2019-04-04 12:39:57355test(t => {
Soares Chen6a9b7812017-06-08 13:25:47356 const pc = new RTCPeerConnection();
youennf0e97d092019-03-23 18:02:56357 t.add_cleanup(() => pc.close());
358
359 const dc = pc.createDataChannel('', { priority: 'high' });
360 assert_equals(dc.priority, 'high');
Soares Chen6a9b7812017-06-08 13:25:47361}, 'createDataChannel with priority "high" should succeed');
362
Harald Alvestrandf6cf3bd2019-04-04 12:39:57363test(t => {
Soares Chen6a9b7812017-06-08 13:25:47364 const pc = new RTCPeerConnection();
youennf0e97d092019-03-23 18:02:56365 t.add_cleanup(() => pc.close());
366
Soares Chen6a9b7812017-06-08 13:25:47367 assert_throws(new TypeError(),
368 () => pc.createDataChannel('', { priority: 'invalid' }));
369}, 'createDataChannel with invalid priority should throw TypeError');
370
371/*
372 6.2. createDataChannel
youennf0e97d092019-03-23 18:02:56373 5. If [[DataChannelLabel]] is longer than 65535 bytes, throw a TypeError.
374 */
Harald Alvestrandf6cf3bd2019-04-04 12:39:57375test(t => {
Soares Chen6a9b7812017-06-08 13:25:47376 const pc = new RTCPeerConnection();
youennf0e97d092019-03-23 18:02:56377 t.add_cleanup(() => pc.close());
378
Soares Chen6a9b7812017-06-08 13:25:47379 assert_throws(new TypeError(), () =>
youennf0e97d092019-03-23 18:02:56380 pc.createDataChannel('l'.repeat(65536)));
381
382 assert_throws(new TypeError(), () =>
383 pc.createDataChannel('l'.repeat(65536), {
384 negotiated: true,
385 id: 42
Soares Chen6a9b7812017-06-08 13:25:47386 }));
youennf0e97d092019-03-23 18:02:56387}, 'createDataChannel with too long label should throw TypeError');
388
Harald Alvestrandf6cf3bd2019-04-04 12:39:57389test(t => {
youennf0e97d092019-03-23 18:02:56390 const pc = new RTCPeerConnection();
391 t.add_cleanup(() => pc.close());
392
393 assert_throws(new TypeError(), () =>
394 pc.createDataChannel('\u00b5'.repeat(32768)));
395
396 assert_throws(new TypeError(), () =>
397 pc.createDataChannel('\u00b5'.repeat(32768), {
398 negotiated: true,
399 id: 42
400 }));
401}, 'createDataChannel with too long label (2 byte unicode) should throw TypeError');
402
403/*
404 6.2. label
405 [...] Scripts are allowed to create multiple RTCDataChannel objects with the same label.
406 [...]
407 */
408test(t => {
409 const pc = new RTCPeerConnection();
410 t.add_cleanup(() => pc.close());
411
412 const label = 'test';
413
414 pc.createDataChannel(label);
415 pc.createDataChannel(label);
416}, 'createDataChannel with same label used twice should not throw');
Soares Chen6a9b7812017-06-08 13:25:47417
418/*
419 6.2. createDataChannel
youennf0e97d092019-03-23 18:02:56420 13. If [[DataChannelProtocol]] is longer than 65535 bytes long, throw a TypeError.
Soares Chen6a9b7812017-06-08 13:25:47421 */
youennf0e97d092019-03-23 18:02:56422
Harald Alvestrandf6cf3bd2019-04-04 12:39:57423test(t => {
youennf0e97d092019-03-23 18:02:56424 const pc = new RTCPeerConnection;
Harald Alvestrandf6cf3bd2019-04-04 12:39:57425 t.add_cleanup(() => pc.close());
426 const channel = pc.createDataChannel('', { negotiated: true, id: 42 });
youennf0e97d092019-03-23 18:02:56427 assert_equals(channel.negotiated, true);
Harald Alvestrandf6cf3bd2019-04-04 12:39:57428}, 'createDataChannel with negotiated true and id should succeed');
youennf0e97d092019-03-23 18:02:56429
Harald Alvestrandf6cf3bd2019-04-04 12:39:57430test(t => {
Soares Chen6a9b7812017-06-08 13:25:47431 const pc = new RTCPeerConnection();
youennf0e97d092019-03-23 18:02:56432 t.add_cleanup(() => pc.close());
433
Soares Chen6a9b7812017-06-08 13:25:47434 assert_throws(new TypeError(), () =>
435 pc.createDataChannel('', {
youennf0e97d092019-03-23 18:02:56436 protocol: 'p'.repeat(65536)
Soares Chen6a9b7812017-06-08 13:25:47437 }));
Soares Chen6a9b7812017-06-08 13:25:47438
youennf0e97d092019-03-23 18:02:56439 assert_throws(new TypeError(), () =>
440 pc.createDataChannel('', {
441 protocol: 'p'.repeat(65536),
442 negotiated: true,
443 id: 42
444 }));
445}, 'createDataChannel with too long protocol should throw TypeError');
446
Harald Alvestrandf6cf3bd2019-04-04 12:39:57447test(t => {
Soares Chen6a9b7812017-06-08 13:25:47448 const pc = new RTCPeerConnection();
youennf0e97d092019-03-23 18:02:56449 t.add_cleanup(() => pc.close());
Soares Chen199b65f2017-06-21 09:20:39450
youennf0e97d092019-03-23 18:02:56451 assert_throws(new TypeError(), () =>
452 pc.createDataChannel('', {
453 protocol: '\u00b6'.repeat(32768)
454 }));
455
456 assert_throws(new TypeError(), () =>
457 pc.createDataChannel('', {
458 protocol: '\u00b6'.repeat(32768),
459 negotiated: true,
460 id: 42
461 }));
462}, 'createDataChannel with too long protocol (2 byte unicode) should throw TypeError');
463
Harald Alvestrandf6cf3bd2019-04-04 12:39:57464test(t => {
youennf0e97d092019-03-23 18:02:56465 const pc = new RTCPeerConnection();
466 t.add_cleanup(() => pc.close());
467
468 const label = 'l'.repeat(65535);
469 const protocol = 'p'.repeat(65535);
470
471 const dc = pc.createDataChannel(label, {
472 protocol: protocol
Soares Chen6a9b7812017-06-08 13:25:47473 });
Soares Chen199b65f2017-06-21 09:20:39474
youennf0e97d092019-03-23 18:02:56475 assert_equals(dc.label, label);
476 assert_equals(dc.protocol, protocol);
477}, 'createDataChannel with maximum length label and protocol should succeed');
478
479/*
480 6.2 createDataChannel
481 15. Let channel have an [[DataChannelId]] internal slot initialized to option's id member,
482 if it is present and [[Negotiated]] is true, otherwise null.
483
484 NOTE
485 This means the id member will be ignored if the data channel is negotiated in-band; this
486 is intentional. Data channels negotiated in-band should have IDs selected based on the
487 DTLS role, as specified in [RTCWEB-DATA-PROTOCOL].
488 */
Harald Alvestrandf6cf3bd2019-04-04 12:39:57489test(t => {
youennf0e97d092019-03-23 18:02:56490 const pc = new RTCPeerConnection;
491 t.add_cleanup(() => pc.close());
492
493 const dc = pc.createDataChannel('', {
494 negotiated: false,
495 });
496 assert_equals(dc.negotiated, false, 'Expect dc.negotiated to be false');
497}, 'createDataChannel with negotiated false should succeed');
498
Harald Alvestrandf6cf3bd2019-04-04 12:39:57499test(t => {
youennf0e97d092019-03-23 18:02:56500 const pc = new RTCPeerConnection;
501 t.add_cleanup(() => pc.close());
502
503 const dc = pc.createDataChannel('', {
504 negotiated: false,
505 id: 42
506 });
507 assert_equals(dc.negotiated, false, 'Expect dc.negotiated to be false');
508 assert_equals(dc.id, null, 'Expect dc.id to be ignored (null)');
509}, 'createDataChannel with negotiated false and id 42 should ignore the id');
510
511/*
512 6.2. createDataChannel
513 16. If [[Negotiated]] is true and [[DataChannelId]] is null, throw a TypeError.
514 */
Harald Alvestrandf6cf3bd2019-04-04 12:39:57515test(t => {
youennf0e97d092019-03-23 18:02:56516 const pc = new RTCPeerConnection();
517 t.add_cleanup(() => pc.close());
518
519 assert_throws(new TypeError(), () =>
520 pc.createDataChannel('test', {
521 negotiated: true
522 }));
523}, 'createDataChannel with negotiated true and id not defined should throw TypeError');
524
Soares Chen6a9b7812017-06-08 13:25:47525/*
Soares Chen199b65f2017-06-21 09:20:39526 4.4.1.6. Set the RTCSessionSessionDescription
527 2.2.6. If description is of type "answer" or "pranswer", then run the
528 following steps:
youennf0e97d092019-03-23 18:02:56529 3. If description negotiates the DTLS role of the SCTP transport, and there is an
530 RTCDataChannel with a null id, then generate an ID according to
531 [RTCWEB-DATA-PROTOCOL]. [...]
Soares Chen199b65f2017-06-21 09:20:39532
youennf0e97d092019-03-23 18:02:56533 6.1. createDataChannel
534 21. If the [[DataChannelId]] slot is null (due to no ID being passed into
535 createDataChannel, or [[Negotiated]] being false), and the DTLS role of the SCTP
536 transport has already been negotiated, then initialize [[DataChannelId]] to a value
537 generated by the user agent, according to [RTCWEB-DATA-PROTOCOL], and skip
538 to the next step. If no available ID could be generated, or if the value of the
539 [[DataChannelId]] slot is being used by an existing RTCDataChannel, throw an
540 OperationError exception.
541
542 Note
543 If the [[DataChannelId]] slot is null after this step, it will be populated once
544 the DTLS role is determined during the process of setting an RTCSessionDescription.
Soares Chen199b65f2017-06-21 09:20:39545 */
Harald Alvestrandf6cf3bd2019-04-04 12:39:57546promise_test(async t => {
youennf0e97d092019-03-23 18:02:56547 const pc1 = new RTCPeerConnection();
548 const pc2 = new RTCPeerConnection();
549 t.add_cleanup(() => pc1.close());
550 t.add_cleanup(() => pc2.close());
551
552 const negotiatedDc = pc1.createDataChannel('negotiated-channel', {
553 negotiated: true,
554 id: 42,
555 });
556 assert_equals(negotiatedDc.id, 42, 'Expect negotiatedDc.id to be 42');
557
558 const dc1 = pc1.createDataChannel('channel');
559 assert_equals(dc1.id, null, 'Expect initial id to be null');
560
561 const offer = await pc1.createOffer();
562 await Promise.all([pc1.setLocalDescription(offer), pc2.setRemoteDescription(offer)]);
563 const answer = await pc2.createAnswer();
564 await pc1.setRemoteDescription(answer);
565
566 assert_not_equals(dc1.id, null,
567 'Expect dc1.id to be assigned after remote description has been set');
568
569 assert_greater_than_equal(dc1.id, 0,
570 'Expect dc1.id to be set to valid unsigned short');
571
572 assert_less_than(dc1.id, 65535,
573 'Expect dc1.id to be set to valid unsigned short');
574
575 const dc2 = pc1.createDataChannel('channel');
576
577 assert_not_equals(dc2.id, null,
578 'Expect dc2.id to be assigned after remote description has been set');
579
580 assert_greater_than_equal(dc2.id, 0,
581 'Expect dc2.id to be set to valid unsigned short');
582
583 assert_less_than(dc2.id, 65535,
584 'Expect dc2.id to be set to valid unsigned short');
585
586 assert_not_equals(dc2, dc1,
587 'Expect channels created from same label to be different');
588
589 assert_equals(dc2.label, dc1.label,
590 'Expect different channels can have the same label but different id');
591
592 assert_not_equals(dc2.id, dc1.id,
593 'Expect different channels can have the same label but different id');
594
595 assert_equals(negotiatedDc.id, 42,
596 'Expect negotiatedDc.id to be 42 after remote description has been set');
597}, 'Channels created (after setRemoteDescription) should have id assigned');
598
Harald Alvestrandf6cf3bd2019-04-04 12:39:57599test(t => {
Soares Chen199b65f2017-06-21 09:20:39600 const pc = new RTCPeerConnection();
Philipp Hancke1622a022018-06-11 10:00:53601 t.add_cleanup(() => pc.close());
Soares Chen199b65f2017-06-21 09:20:39602
youennf0e97d092019-03-23 18:02:56603 const dc1 = pc.createDataChannel('channel-1', {
604 negotiated: true,
605 id: 42,
Soares Chen199b65f2017-06-21 09:20:39606 });
youennf0e97d092019-03-23 18:02:56607 assert_equals(dc1.id, 42,
608 'Expect dc1.id to be 42');
609
610 const dc2 = pc.createDataChannel('channel-2', {
611 negotiated: true,
612 id: 43,
613 });
614 assert_equals(dc2.id, 43,
615 'Expect dc2.id to be 43');
616
617 assert_throws('OperationError', () =>
618 pc.createDataChannel('channel-3', {
619 negotiated: true,
620 id: 42,
621 }));
622
623}, 'Reusing a data channel id that is in use should throw OperationError');
624
625// We've seen implementations behaving differently before and after the connection has been
626// established.
Harald Alvestrandf6cf3bd2019-04-04 12:39:57627promise_test(async t => {
youennf0e97d092019-03-23 18:02:56628 const pc1 = new RTCPeerConnection();
629 const pc2 = new RTCPeerConnection();
630 t.add_cleanup(() => pc1.close());
631 t.add_cleanup(() => pc2.close());
632
633 const dc1 = pc1.createDataChannel('channel-1', {
634 negotiated: true,
635 id: 42,
636 });
637 assert_equals(dc1.id, 42, 'Expect dc1.id to be 42');
638
639 const dc2 = pc1.createDataChannel('channel-2', {
640 negotiated: true,
641 id: 43,
642 });
643 assert_equals(dc2.id, 43, 'Expect dc2.id to be 43');
644
645 const offer = await pc1.createOffer();
646 await Promise.all([pc1.setLocalDescription(offer), pc2.setRemoteDescription(offer)]);
647 const answer = await pc2.createAnswer();
648 await pc1.setRemoteDescription(answer);
649
650 assert_equals(dc1.id, 42, 'Expect dc1.id to be 42');
651
652 assert_equals(dc2.id, 43, 'Expect dc2.id to be 43');
653
654 assert_throws('OperationError', () =>
655 pc1.createDataChannel('channel-3', {
656 negotiated: true,
657 id: 42,
658 }));
659}, 'Reusing a data channel id that is in use (after setRemoteDescription) should throw ' +
660 'OperationError');
661
Harald Alvestrandf6cf3bd2019-04-04 12:39:57662promise_test(async t => {
youennf0e97d092019-03-23 18:02:56663 const pc1 = new RTCPeerConnection();
664 const pc2 = new RTCPeerConnection();
665 t.add_cleanup(() => pc1.close());
666 t.add_cleanup(() => pc2.close());
667
668 const dc1 = pc1.createDataChannel('channel-1');
669
670 const offer = await pc1.createOffer();
671 await Promise.all([pc1.setLocalDescription(offer), pc2.setRemoteDescription(offer)]);
672 const answer = await pc2.createAnswer();
673 await pc1.setRemoteDescription(answer);
674
675 assert_not_equals(dc1.id, null,
676 'Expect dc1.id to be assigned after remote description has been set');
677
678 assert_throws('OperationError', () =>
679 pc1.createDataChannel('channel-2', {
680 negotiated: true,
681 id: dc1.id,
682 }));
683}, 'Reusing a data channel id that is in use (after setRemoteDescription, negotiated via DCEP) ' +
684 'should throw OperationError');
685
686// Based on https://bugzilla.mozilla.org/show_bug.cgi?id=1441723
Harald Alvestrandf6cf3bd2019-04-04 12:39:57687promise_test(async t => {
youennf0e97d092019-03-23 18:02:56688 const pc1 = new RTCPeerConnection();
689 const pc2 = new RTCPeerConnection();
690 t.add_cleanup(() => pc1.close());
691 t.add_cleanup(() => pc2.close());
692
693 await createDataChannelPair(pc1, pc2);
694
695 const dc = pc1.createDataChannel('');
696 assert_equals(dc.readyState, 'connecting', 'Channel should be in the connecting state');
697}, 'New data channel should be in the connecting state after creation (after connection ' +
698 'establishment)');
Soares Chen199b65f2017-06-21 09:20:39699
700/*
Soares Chen6a9b7812017-06-08 13:25:47701 Untestable
Soares Chen199b65f2017-06-21 09:20:39702 6.1. createDataChannel
youennf0e97d092019-03-23 18:02:56703 19. If a setting, either [[MaxPacketLifeTime]] or [[MaxRetransmits]], has been set to
704 indicate unreliable mode, and that value exceeds the maximum value supported
705 by the user agent, the value MUST be set to the user agents maximum value.
Soares Chen199b65f2017-06-21 09:20:39706
youennf0e97d092019-03-23 18:02:56707 23. Return channel and continue the following steps in parallel.
708 24. Create channel's associated underlying data transport and configure
Soares Chen199b65f2017-06-21 09:20:39709 it according to the relevant properties of channel.
710
711 Tested in RTCPeerConnection-onnegotiationneeded.html
youennf0e97d092019-03-23 18:02:56712 22. If channel is the first RTCDataChannel created on connection, update the
713 negotiation-needed flag for connection.
Soares Chen199b65f2017-06-21 09:20:39714
youennf0e97d092019-03-23 18:02:56715 Tested in RTCDataChannel-id.html
716 - Odd/even rules for '.id'
Soares Chen199b65f2017-06-21 09:20:39717
youennf0e97d092019-03-23 18:02:56718 Tested in RTCDataChannel-dcep.html
719 - Transmission of '.label' and further options
Soares Chen199b65f2017-06-21 09:20:39720*/
foolip72525e92017-04-24 09:31:03721</script>