blob: 67b40c9ed8ea44d9559a318619206531c01b32a7 [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
moz-wptsync-botb0d2aa52019-10-02 14:44:1010const stopTracks = (...streams) => {
11 streams.forEach(stream => stream.getTracks().forEach(track => track.stop()));
12};
13
youennf0e97d092019-03-23 18:02:5614// Test is based on the following revision:
15// https://rawgit.com/w3c/webrtc-pc/1cc5bfc3ff18741033d804c4a71f7891242fb5b3/webrtc.html
Soares Chen6a9b7812017-06-08 13:25:4716
17/*
18 6.1. RTCPeerConnection Interface Extensions
19
20 partial interface RTCPeerConnection {
youennf0e97d092019-03-23 18:02:5621 [...]
22 RTCDataChannel createDataChannel(USVString label,
23 optional RTCDataChannelInit dataChannelDict);
24 [...]
Soares Chen6a9b7812017-06-08 13:25:4725 };
26
27 6.2. RTCDataChannel
Soares Chen199b65f2017-06-21 09:20:3928
29 interface RTCDataChannel : EventTarget {
youennf0e97d092019-03-23 18:02:5630 readonly attribute USVString label;
31 readonly attribute boolean ordered;
32 readonly attribute unsigned short? maxPacketLifeTime;
33 readonly attribute unsigned short? maxRetransmits;
34 readonly attribute USVString protocol;
35 readonly attribute boolean negotiated;
36 readonly attribute unsigned short? id;
youennf0e97d092019-03-23 18:02:5637 readonly attribute RTCDataChannelState readyState;
38 readonly attribute unsigned long bufferedAmount;
39 attribute unsigned long bufferedAmountLowThreshold;
40 [...]
41 attribute DOMString binaryType;
42 [...]
Soares Chen199b65f2017-06-21 09:20:3943 };
44
Soares Chen6a9b7812017-06-08 13:25:4745 dictionary RTCDataChannelInit {
youennf0e97d092019-03-23 18:02:5646 boolean ordered = true;
47 unsigned short maxPacketLifeTime;
48 unsigned short maxRetransmits;
49 USVString protocol = "";
50 boolean negotiated = false;
51 [EnforceRange]
52 unsigned short id;
Soares Chen199b65f2017-06-21 09:20:3953 };
Soares Chen6a9b7812017-06-08 13:25:4754 */
foolip72525e92017-04-24 09:31:0355
Harald Alvestrandf6cf3bd2019-04-04 12:39:5756test(t => {
Soares Chen6a9b7812017-06-08 13:25:4757 const pc = new RTCPeerConnection();
youennf0e97d092019-03-23 18:02:5658 t.add_cleanup(() => pc.close());
59
Soares Chen6a9b7812017-06-08 13:25:4760 assert_equals(pc.createDataChannel.length, 1);
Boris Zbarsky71bf0202020-01-21 04:26:3761 assert_throws_js(TypeError, () => pc.createDataChannel());
Soares Chen6a9b7812017-06-08 13:25:4762}, 'createDataChannel with no argument should throw TypeError');
63
64/*
65 6.2. createDataChannel
66 2. If connection's [[isClosed]] slot is true, throw an InvalidStateError.
67 */
Harald Alvestrandf6cf3bd2019-04-04 12:39:5768test(t => {
Soares Chen6a9b7812017-06-08 13:25:4769 const pc = new RTCPeerConnection();
foolip72525e92017-04-24 09:31:0370 pc.close();
71 assert_equals(pc.signalingState, 'closed', 'signaling state');
Stephen McGruerd5103042020-01-23 21:45:4572 assert_throws_dom('InvalidStateError', () => pc.createDataChannel(''));
Soares Chen6a9b7812017-06-08 13:25:4773}, 'createDataChannel with closed connection should throw InvalidStateError');
foolip72525e92017-04-24 09:31:0374
Soares Chen6a9b7812017-06-08 13:25:4775/*
Soares Chen199b65f2017-06-21 09:20:3976 6.1. createDataChannel
youennf0e97d092019-03-23 18:02:5677 4. Let channel have a [[DataChannelLabel]] internal slot initialized to the value of the
Soares Chen199b65f2017-06-21 09:20:3978 first argument.
youennf0e97d092019-03-23 18:02:5679 6. Let options be the second argument.
80 7. Let channel have an [[MaxPacketLifeTime]] internal slot initialized to
Soares Chen199b65f2017-06-21 09:20:3981 option's maxPacketLifeTime member, if present, otherwise null.
youennf0e97d092019-03-23 18:02:5682 8. Let channel have a [[ReadyState]] internal slot initialized to "connecting".
83 9. Let channel have a [[BufferedAmount]] internal slot initialized to 0.
84 10. Let channel have an [[MaxRetransmits]] internal slot initialized to
Soares Chen199b65f2017-06-21 09:20:3985 option's maxRetransmits member, if present, otherwise null.
youennf0e97d092019-03-23 18:02:5686 11. Let channel have an [[Ordered]] internal slot initialized to option's
Soares Chen199b65f2017-06-21 09:20:3987 ordered member.
youennf0e97d092019-03-23 18:02:5688 12. Let channel have a [[DataChannelProtocol]] internal slot initialized to option's
Soares Chen199b65f2017-06-21 09:20:3989 protocol member.
youennf0e97d092019-03-23 18:02:5690 14. Let channel have a [[Negotiated]] internal slot initialized to option's negotiated
91 member.
92 15. Let channel have an [[DataChannelId]] internal slot initialized to option's id
93 member, if it is present and [[Negotiated]] is true, otherwise null.
youennf0e97d092019-03-23 18:02:5694 21. If the [[DataChannelId]] slot is null (due to no ID being passed into
95 createDataChannel, or [[Negotiated]] being false), and the DTLS role of the SCTP
96 transport has already been negotiated, then initialize [[DataChannelId]] to a value
97 generated by the user agent, according to [RTCWEB-DATA-PROTOCOL], and skip
98 to the next step. If no available ID could be generated, or if the value of the
99 [[DataChannelId]] slot is being used by an existing RTCDataChannel, throw an
100 OperationError exception.
101
102 Note
103 If the [[DataChannelId]] slot is null after this step, it will be populated once
104 the DTLS role is determined during the process of setting an RTCSessionDescription.
105 22. If channel is the first RTCDataChannel created on connection, update the
106 negotiation-needed flag for connection.
107
Soares Chen199b65f2017-06-21 09:20:39108
109 6.2. RTCDataChannel
110
111 A RTCDataChannel, created with createDataChannel or dispatched via a
112 RTCDataChannelEvent, MUST initially be in the connecting state
113
youennf0e97d092019-03-23 18:02:56114 bufferedAmountLowThreshold
115 [...] The bufferedAmountLowThreshold is initially zero on each new RTCDataChannel,
116 but the application may change its value at any time.
117
Soares Chen199b65f2017-06-21 09:20:39118 binaryType
youennf0e97d092019-03-23 18:02:56119 [...] When a RTCDataChannel object is created, the binaryType attribute MUST
Soares Chen199b65f2017-06-21 09:20:39120 be initialized to the string "blob".
Soares Chen6a9b7812017-06-08 13:25:47121 */
Harald Alvestrandf6cf3bd2019-04-04 12:39:57122test(t => {
Soares Chen6a9b7812017-06-08 13:25:47123 const pc = new RTCPeerConnection();
youennf0e97d092019-03-23 18:02:56124 t.add_cleanup(() => pc.close());
Soares Chen199b65f2017-06-21 09:20:39125
youennf0e97d092019-03-23 18:02:56126 const dc = pc.createDataChannel('');
127
128 assert_true(dc instanceof RTCDataChannel, 'is RTCDataChannel');
129 assert_equals(dc.label, '');
130 assert_equals(dc.ordered, true);
131 assert_equals(dc.maxPacketLifeTime, null);
132 assert_equals(dc.maxRetransmits, null);
133 assert_equals(dc.protocol, '');
134 assert_equals(dc.negotiated, false);
Taylor Brandstetterce56f0e2017-05-09 13:53:14135 // Since no offer/answer exchange has occurred yet, the DTLS role is unknown
136 // and so the ID should be null.
youennf0e97d092019-03-23 18:02:56137 assert_equals(dc.id, null);
youennf0e97d092019-03-23 18:02:56138 assert_equals(dc.readyState, 'connecting');
139 assert_equals(dc.bufferedAmount, 0);
140 assert_equals(dc.bufferedAmountLowThreshold, 0);
141 assert_equals(dc.binaryType, 'blob');
Soares Chen6a9b7812017-06-08 13:25:47142}, 'createDataChannel attribute default values');
foolip72525e92017-04-24 09:31:03143
Harald Alvestrandf6cf3bd2019-04-04 12:39:57144test(t => {
Soares Chen199b65f2017-06-21 09:20:39145 const pc = new RTCPeerConnection();
youennf0e97d092019-03-23 18:02:56146 t.add_cleanup(() => pc.close());
147
148 const dc = pc.createDataChannel('test', {
Soares Chen199b65f2017-06-21 09:20:39149 ordered: false,
Soares Chen199b65f2017-06-21 09:20:39150 maxRetransmits: 1,
Harald Alvestrandf6cf3bd2019-04-04 12:39:57151 // Note: maxPacketLifeTime is not set in this test.
Soares Chen199b65f2017-06-21 09:20:39152 protocol: 'custom',
153 negotiated: true,
Jan-Ivar Bruaroeye6d95ba2020-01-21 20:01:28154 id: 3
Soares Chen199b65f2017-06-21 09:20:39155 });
156
youennf0e97d092019-03-23 18:02:56157 assert_true(dc instanceof RTCDataChannel, 'is RTCDataChannel');
158 assert_equals(dc.label, 'test');
159 assert_equals(dc.ordered, false);
160 assert_equals(dc.maxPacketLifeTime, null);
161 assert_equals(dc.maxRetransmits, 1);
162 assert_equals(dc.protocol, 'custom');
163 assert_equals(dc.negotiated, true);
164 assert_equals(dc.id, 3);
youennf0e97d092019-03-23 18:02:56165 assert_equals(dc.readyState, 'connecting');
166 assert_equals(dc.bufferedAmount, 0);
167 assert_equals(dc.bufferedAmountLowThreshold, 0);
168 assert_equals(dc.binaryType, 'blob');
Harald Alvestrandf6cf3bd2019-04-04 12:39:57169
170 const dc2 = pc.createDataChannel('test2', {
171 ordered: false,
172 maxPacketLifeTime: 42
173 });
174 assert_equals(dc2.label, 'test2');
175 assert_equals(dc2.maxPacketLifeTime, 42);
Jan-Ivar Bruaroey0d385112020-01-21 20:01:23176 assert_equals(dc2.maxRetransmits, null);
Soares Chen199b65f2017-06-21 09:20:39177}, 'createDataChannel with provided parameters should initialize attributes to provided values');
Soares Chen6a9b7812017-06-08 13:25:47178
179/*
180 6.2. createDataChannel
youennf0e97d092019-03-23 18:02:56181 4. Let channel have a [[DataChannelLabel]] internal slot initialized to the value of the
Soares Chen199b65f2017-06-21 09:20:39182 first argument.
Soares Chen6a9b7812017-06-08 13:25:47183
184 [ECMA262] 7.1.12. ToString(argument)
185 undefined -> "undefined"
186 null -> "null"
187
188 [WebIDL] 3.10.15. Convert a DOMString to a sequence of Unicode scalar values
189 */
foolip72525e92017-04-24 09:31:03190const labels = [
191 ['"foo"', 'foo', 'foo'],
192 ['null', null, 'null'],
193 ['undefined', undefined, 'undefined'],
194 ['lone surrogate', '\uD800', '\uFFFD'],
195];
196for (const [description, label, expected] of labels) {
Harald Alvestrandf6cf3bd2019-04-04 12:39:57197 test(t => {
foolip72525e92017-04-24 09:31:03198 const pc = new RTCPeerConnection;
youennf0e97d092019-03-23 18:02:56199 t.add_cleanup(() => pc.close());
200
201 const dc = pc.createDataChannel(label);
202 assert_equals(dc.label, expected);
Soares Chen6a9b7812017-06-08 13:25:47203 }, `createDataChannel with label ${description} should succeed`);
foolip72525e92017-04-24 09:31:03204}
205
Soares Chen6a9b7812017-06-08 13:25:47206/*
207 6.2. RTCDataChannel
Soares Chen199b65f2017-06-21 09:20:39208 createDataChannel
youennf0e97d092019-03-23 18:02:56209 11. Let channel have an [[Ordered]] internal slot initialized to option's
Soares Chen199b65f2017-06-21 09:20:39210 ordered member.
Soares Chen6a9b7812017-06-08 13:25:47211 */
Harald Alvestrandf6cf3bd2019-04-04 12:39:57212test(t => {
Soares Chen6a9b7812017-06-08 13:25:47213 const pc = new RTCPeerConnection();
youennf0e97d092019-03-23 18:02:56214 t.add_cleanup(() => pc.close());
215
216 const dc = pc.createDataChannel('', { ordered: false });
217 assert_equals(dc.ordered, false);
Soares Chen6a9b7812017-06-08 13:25:47218}, 'createDataChannel with ordered false should succeed');
foolip72525e92017-04-24 09:31:03219
220// true as the default value of a boolean is confusing because null is converted
221// to false while undefined is converted to true.
Harald Alvestrandf6cf3bd2019-04-04 12:39:57222test(t => {
Soares Chen6a9b7812017-06-08 13:25:47223 const pc = new RTCPeerConnection();
youennf0e97d092019-03-23 18:02:56224 t.add_cleanup(() => pc.close());
225
226 const dc1 = pc.createDataChannel('', { ordered: null });
227 assert_equals(dc1.ordered, false);
228 const dc2 = pc.createDataChannel('', { ordered: undefined });
229 assert_equals(dc2.ordered, true);
Soares Chen6a9b7812017-06-08 13:25:47230}, 'createDataChannel with ordered null/undefined should succeed');
foolip72525e92017-04-24 09:31:03231
Soares Chen6a9b7812017-06-08 13:25:47232/*
233 6.2. RTCDataChannel
Soares Chen199b65f2017-06-21 09:20:39234 createDataChannel
youennf0e97d092019-03-23 18:02:56235 7. Let channel have an [[MaxPacketLifeTime]] internal slot initialized to
Soares Chen199b65f2017-06-21 09:20:39236 option's maxPacketLifeTime member, if present, otherwise null.
Soares Chen6a9b7812017-06-08 13:25:47237 */
Harald Alvestrandf6cf3bd2019-04-04 12:39:57238test(t => {
foolip72525e92017-04-24 09:31:03239 const pc = new RTCPeerConnection;
youennf0e97d092019-03-23 18:02:56240 t.add_cleanup(() => pc.close());
241
242 const dc = pc.createDataChannel('', { maxPacketLifeTime: 0 });
243 assert_equals(dc.maxPacketLifeTime, 0);
Soares Chen6a9b7812017-06-08 13:25:47244}, 'createDataChannel with maxPacketLifeTime 0 should succeed');
foolip72525e92017-04-24 09:31:03245
Soares Chen6a9b7812017-06-08 13:25:47246/*
247 6.2. RTCDataChannel
Soares Chen199b65f2017-06-21 09:20:39248 createDataChannel
youennf0e97d092019-03-23 18:02:56249 10. Let channel have an [[MaxRetransmits]] internal slot initialized to
Soares Chen199b65f2017-06-21 09:20:39250 option's maxRetransmits member, if present, otherwise null.
Soares Chen6a9b7812017-06-08 13:25:47251 */
Harald Alvestrandf6cf3bd2019-04-04 12:39:57252test(t => {
foolip72525e92017-04-24 09:31:03253 const pc = new RTCPeerConnection;
youennf0e97d092019-03-23 18:02:56254 t.add_cleanup(() => pc.close());
255
256 const dc = pc.createDataChannel('', { maxRetransmits: 0 });
257 assert_equals(dc.maxRetransmits, 0);
Soares Chen6a9b7812017-06-08 13:25:47258}, 'createDataChannel with maxRetransmits 0 should succeed');
foolip72525e92017-04-24 09:31:03259
Soares Chen6a9b7812017-06-08 13:25:47260/*
261 6.2. createDataChannel
youennf0e97d092019-03-23 18:02:56262 18. If both [[MaxPacketLifeTime]] and [[MaxRetransmits]] attributes are set (not null),
263 throw a TypeError.
Soares Chen6a9b7812017-06-08 13:25:47264 */
Harald Alvestrandf6cf3bd2019-04-04 12:39:57265test(t => {
foolip72525e92017-04-24 09:31:03266 const pc = new RTCPeerConnection;
youennf0e97d092019-03-23 18:02:56267 t.add_cleanup(() => pc.close());
268
269 pc.createDataChannel('', {
Harald Alvestrandf6cf3bd2019-04-04 12:39:57270 maxPacketLifeTime: undefined,
271 maxRetransmits: undefined
youennf0e97d092019-03-23 18:02:56272 });
Harald Alvestrandf6cf3bd2019-04-04 12:39:57273}, 'createDataChannel with both maxPacketLifeTime and maxRetransmits undefined should succeed');
youennf0e97d092019-03-23 18:02:56274
Harald Alvestrandf6cf3bd2019-04-04 12:39:57275test(t => {
youennf0e97d092019-03-23 18:02:56276 const pc = new RTCPeerConnection;
277 t.add_cleanup(() => pc.close());
278
Boris Zbarsky71bf0202020-01-21 04:26:37279 assert_throws_js(TypeError, () => pc.createDataChannel('', {
foolip72525e92017-04-24 09:31:03280 maxPacketLifeTime: 0,
281 maxRetransmits: 0
282 }));
Boris Zbarsky71bf0202020-01-21 04:26:37283 assert_throws_js(TypeError, () => pc.createDataChannel('', {
youennf0e97d092019-03-23 18:02:56284 maxPacketLifeTime: 42,
285 maxRetransmits: 42
286 }));
287}, 'createDataChannel with both maxPacketLifeTime and maxRetransmits should throw TypeError');
foolip72525e92017-04-24 09:31:03288
Soares Chen6a9b7812017-06-08 13:25:47289/*
290 6.2. RTCDataChannel
Soares Chen199b65f2017-06-21 09:20:39291 createDataChannel
youennf0e97d092019-03-23 18:02:56292 12. Let channel have a [[DataChannelProtocol]] internal slot initialized to option's
Soares Chen199b65f2017-06-21 09:20:39293 protocol member.
Soares Chen6a9b7812017-06-08 13:25:47294 */
foolip72525e92017-04-24 09:31:03295const protocols = [
296 ['"foo"', 'foo', 'foo'],
297 ['null', null, 'null'],
298 ['undefined', undefined, ''],
299 ['lone surrogate', '\uD800', '\uFFFD'],
300];
301for (const [description, protocol, expected] of protocols) {
Harald Alvestrandf6cf3bd2019-04-04 12:39:57302 test(t => {
foolip72525e92017-04-24 09:31:03303 const pc = new RTCPeerConnection;
youennf0e97d092019-03-23 18:02:56304 t.add_cleanup(() => pc.close());
305
306 const dc = pc.createDataChannel('', { protocol });
307 assert_equals(dc.protocol, expected);
Soares Chen6a9b7812017-06-08 13:25:47308 }, `createDataChannel with protocol ${description} should succeed`);
foolip72525e92017-04-24 09:31:03309}
310
Soares Chen6a9b7812017-06-08 13:25:47311/*
312 6.2. RTCDataChannel
Soares Chen199b65f2017-06-21 09:20:39313 createDataChannel
youennf0e97d092019-03-23 18:02:56314 20. If [[DataChannelId]] is equal to 65535, which is greater than the maximum allowed
315 ID of 65534 but still qualifies as an unsigned short, throw a TypeError.
Soares Chen6a9b7812017-06-08 13:25:47316 */
Boris Zbarskyf1d2a712020-01-21 04:24:46317for (const id of [0, 1, 65534, 65535]) {
318 test((t) => {
319 const pc = new RTCPeerConnection();
320 t.add_cleanup(() => pc.close());
321 const dc = pc.createDataChannel('', { id });
322 assert_equals(dc.id, null);
323 }, `createDataChannel with id ${id} and negotiated not set should succeed, but not set the channel's id`);
324}
325
Soares Chen6a9b7812017-06-08 13:25:47326for (const id of [0, 1, 65534]) {
Harald Alvestrandf6cf3bd2019-04-04 12:39:57327 test(t => {
Soares Chen6a9b7812017-06-08 13:25:47328 const pc = new RTCPeerConnection();
youennf0e97d092019-03-23 18:02:56329 t.add_cleanup(() => pc.close());
330
Harald Alvestrand267d5dd2019-04-02 12:40:16331 const dc = pc.createDataChannel('', { 'negotiated': true, 'id': id });
youennf0e97d092019-03-23 18:02:56332 assert_equals(dc.id, id);
Boris Zbarskyf1d2a712020-01-21 04:24:46333 }, `createDataChannel with id ${id} and negotiated true should succeed, and set the channel's id`);
334}
335
336for (const id of [-1, 65536]) {
337 test((t) => {
338 const pc = new RTCPeerConnection();
339 t.add_cleanup(() => pc.close());
Boris Zbarsky71bf0202020-01-21 04:26:37340 assert_throws_js(TypeError, () => pc.createDataChannel('', { id }));
Boris Zbarskyf1d2a712020-01-21 04:24:46341 }, `createDataChannel with id ${id} and negotiated not set should throw TypeError`);
foolip72525e92017-04-24 09:31:03342}
343
Soares Chen6a9b7812017-06-08 13:25:47344for (const id of [-1, 65535, 65536]) {
Harald Alvestrandf6cf3bd2019-04-04 12:39:57345 test(t => {
Soares Chen6a9b7812017-06-08 13:25:47346 const pc = new RTCPeerConnection();
youennf0e97d092019-03-23 18:02:56347 t.add_cleanup(() => pc.close());
348
Boris Zbarsky71bf0202020-01-21 04:26:37349 assert_throws_js(TypeError, () => pc.createDataChannel('',
Harald Alvestrand267d5dd2019-04-02 12:40:16350 { 'negotiated': true, 'id': id }));
Soares Chen6a9b7812017-06-08 13:25:47351 }, `createDataChannel with id ${id} should throw TypeError`);
352}
353
354/*
Soares Chen6a9b7812017-06-08 13:25:47355 6.2. createDataChannel
youennf0e97d092019-03-23 18:02:56356 5. If [[DataChannelLabel]] is longer than 65535 bytes, throw a TypeError.
357 */
Harald Alvestrandf6cf3bd2019-04-04 12:39:57358test(t => {
Soares Chen6a9b7812017-06-08 13:25:47359 const pc = new RTCPeerConnection();
youennf0e97d092019-03-23 18:02:56360 t.add_cleanup(() => pc.close());
361
Boris Zbarsky71bf0202020-01-21 04:26:37362 assert_throws_js(TypeError, () =>
youennf0e97d092019-03-23 18:02:56363 pc.createDataChannel('l'.repeat(65536)));
364
Boris Zbarsky71bf0202020-01-21 04:26:37365 assert_throws_js(TypeError, () =>
youennf0e97d092019-03-23 18:02:56366 pc.createDataChannel('l'.repeat(65536), {
367 negotiated: true,
368 id: 42
Soares Chen6a9b7812017-06-08 13:25:47369 }));
youennf0e97d092019-03-23 18:02:56370}, 'createDataChannel with too long label should throw TypeError');
371
Harald Alvestrandf6cf3bd2019-04-04 12:39:57372test(t => {
youennf0e97d092019-03-23 18:02:56373 const pc = new RTCPeerConnection();
374 t.add_cleanup(() => pc.close());
375
Boris Zbarsky71bf0202020-01-21 04:26:37376 assert_throws_js(TypeError, () =>
youennf0e97d092019-03-23 18:02:56377 pc.createDataChannel('\u00b5'.repeat(32768)));
378
Boris Zbarsky71bf0202020-01-21 04:26:37379 assert_throws_js(TypeError, () =>
youennf0e97d092019-03-23 18:02:56380 pc.createDataChannel('\u00b5'.repeat(32768), {
381 negotiated: true,
382 id: 42
383 }));
384}, 'createDataChannel with too long label (2 byte unicode) should throw TypeError');
385
386/*
387 6.2. label
388 [...] Scripts are allowed to create multiple RTCDataChannel objects with the same label.
389 [...]
390 */
391test(t => {
392 const pc = new RTCPeerConnection();
393 t.add_cleanup(() => pc.close());
394
395 const label = 'test';
396
397 pc.createDataChannel(label);
398 pc.createDataChannel(label);
399}, 'createDataChannel with same label used twice should not throw');
Soares Chen6a9b7812017-06-08 13:25:47400
401/*
402 6.2. createDataChannel
youennf0e97d092019-03-23 18:02:56403 13. If [[DataChannelProtocol]] is longer than 65535 bytes long, throw a TypeError.
Soares Chen6a9b7812017-06-08 13:25:47404 */
youennf0e97d092019-03-23 18:02:56405
Harald Alvestrandf6cf3bd2019-04-04 12:39:57406test(t => {
youennf0e97d092019-03-23 18:02:56407 const pc = new RTCPeerConnection;
Harald Alvestrandf6cf3bd2019-04-04 12:39:57408 t.add_cleanup(() => pc.close());
409 const channel = pc.createDataChannel('', { negotiated: true, id: 42 });
youennf0e97d092019-03-23 18:02:56410 assert_equals(channel.negotiated, true);
Harald Alvestrandf6cf3bd2019-04-04 12:39:57411}, 'createDataChannel with negotiated true and id should succeed');
youennf0e97d092019-03-23 18:02:56412
Harald Alvestrandf6cf3bd2019-04-04 12:39:57413test(t => {
Soares Chen6a9b7812017-06-08 13:25:47414 const pc = new RTCPeerConnection();
youennf0e97d092019-03-23 18:02:56415 t.add_cleanup(() => pc.close());
416
Boris Zbarsky71bf0202020-01-21 04:26:37417 assert_throws_js(TypeError, () =>
Soares Chen6a9b7812017-06-08 13:25:47418 pc.createDataChannel('', {
youennf0e97d092019-03-23 18:02:56419 protocol: 'p'.repeat(65536)
Soares Chen6a9b7812017-06-08 13:25:47420 }));
Soares Chen6a9b7812017-06-08 13:25:47421
Boris Zbarsky71bf0202020-01-21 04:26:37422 assert_throws_js(TypeError, () =>
youennf0e97d092019-03-23 18:02:56423 pc.createDataChannel('', {
424 protocol: 'p'.repeat(65536),
425 negotiated: true,
426 id: 42
427 }));
428}, 'createDataChannel with too long protocol should throw TypeError');
429
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());
Soares Chen199b65f2017-06-21 09:20:39433
Boris Zbarsky71bf0202020-01-21 04:26:37434 assert_throws_js(TypeError, () =>
youennf0e97d092019-03-23 18:02:56435 pc.createDataChannel('', {
436 protocol: '\u00b6'.repeat(32768)
437 }));
438
Boris Zbarsky71bf0202020-01-21 04:26:37439 assert_throws_js(TypeError, () =>
youennf0e97d092019-03-23 18:02:56440 pc.createDataChannel('', {
441 protocol: '\u00b6'.repeat(32768),
442 negotiated: true,
443 id: 42
444 }));
445}, 'createDataChannel with too long protocol (2 byte unicode) should throw TypeError');
446
Harald Alvestrandf6cf3bd2019-04-04 12:39:57447test(t => {
youennf0e97d092019-03-23 18:02:56448 const pc = new RTCPeerConnection();
449 t.add_cleanup(() => pc.close());
450
451 const label = 'l'.repeat(65535);
452 const protocol = 'p'.repeat(65535);
453
454 const dc = pc.createDataChannel(label, {
455 protocol: protocol
Soares Chen6a9b7812017-06-08 13:25:47456 });
Soares Chen199b65f2017-06-21 09:20:39457
youennf0e97d092019-03-23 18:02:56458 assert_equals(dc.label, label);
459 assert_equals(dc.protocol, protocol);
460}, 'createDataChannel with maximum length label and protocol should succeed');
461
462/*
463 6.2 createDataChannel
464 15. Let channel have an [[DataChannelId]] internal slot initialized to option's id member,
465 if it is present and [[Negotiated]] is true, otherwise null.
466
467 NOTE
468 This means the id member will be ignored if the data channel is negotiated in-band; this
469 is intentional. Data channels negotiated in-band should have IDs selected based on the
470 DTLS role, as specified in [RTCWEB-DATA-PROTOCOL].
471 */
Harald Alvestrandf6cf3bd2019-04-04 12:39:57472test(t => {
youennf0e97d092019-03-23 18:02:56473 const pc = new RTCPeerConnection;
474 t.add_cleanup(() => pc.close());
475
476 const dc = pc.createDataChannel('', {
477 negotiated: false,
478 });
479 assert_equals(dc.negotiated, false, 'Expect dc.negotiated to be false');
480}, 'createDataChannel with negotiated false should succeed');
481
Harald Alvestrandf6cf3bd2019-04-04 12:39:57482test(t => {
youennf0e97d092019-03-23 18:02:56483 const pc = new RTCPeerConnection;
484 t.add_cleanup(() => pc.close());
485
486 const dc = pc.createDataChannel('', {
487 negotiated: false,
488 id: 42
489 });
490 assert_equals(dc.negotiated, false, 'Expect dc.negotiated to be false');
491 assert_equals(dc.id, null, 'Expect dc.id to be ignored (null)');
492}, 'createDataChannel with negotiated false and id 42 should ignore the id');
493
494/*
495 6.2. createDataChannel
496 16. If [[Negotiated]] is true and [[DataChannelId]] is null, throw a TypeError.
497 */
Harald Alvestrandf6cf3bd2019-04-04 12:39:57498test(t => {
youennf0e97d092019-03-23 18:02:56499 const pc = new RTCPeerConnection();
500 t.add_cleanup(() => pc.close());
501
Boris Zbarsky71bf0202020-01-21 04:26:37502 assert_throws_js(TypeError, () =>
youennf0e97d092019-03-23 18:02:56503 pc.createDataChannel('test', {
504 negotiated: true
505 }));
506}, 'createDataChannel with negotiated true and id not defined should throw TypeError');
507
Soares Chen6a9b7812017-06-08 13:25:47508/*
Soares Chen199b65f2017-06-21 09:20:39509 4.4.1.6. Set the RTCSessionSessionDescription
510 2.2.6. If description is of type "answer" or "pranswer", then run the
511 following steps:
youennf0e97d092019-03-23 18:02:56512 3. If description negotiates the DTLS role of the SCTP transport, and there is an
513 RTCDataChannel with a null id, then generate an ID according to
514 [RTCWEB-DATA-PROTOCOL]. [...]
Soares Chen199b65f2017-06-21 09:20:39515
youennf0e97d092019-03-23 18:02:56516 6.1. createDataChannel
517 21. If the [[DataChannelId]] slot is null (due to no ID being passed into
518 createDataChannel, or [[Negotiated]] being false), and the DTLS role of the SCTP
519 transport has already been negotiated, then initialize [[DataChannelId]] to a value
520 generated by the user agent, according to [RTCWEB-DATA-PROTOCOL], and skip
521 to the next step. If no available ID could be generated, or if the value of the
522 [[DataChannelId]] slot is being used by an existing RTCDataChannel, throw an
523 OperationError exception.
524
525 Note
526 If the [[DataChannelId]] slot is null after this step, it will be populated once
527 the DTLS role is determined during the process of setting an RTCSessionDescription.
Soares Chen199b65f2017-06-21 09:20:39528 */
Harald Alvestrandf6cf3bd2019-04-04 12:39:57529promise_test(async t => {
youennf0e97d092019-03-23 18:02:56530 const pc1 = new RTCPeerConnection();
531 const pc2 = new RTCPeerConnection();
532 t.add_cleanup(() => pc1.close());
533 t.add_cleanup(() => pc2.close());
534
535 const negotiatedDc = pc1.createDataChannel('negotiated-channel', {
536 negotiated: true,
537 id: 42,
538 });
539 assert_equals(negotiatedDc.id, 42, 'Expect negotiatedDc.id to be 42');
540
541 const dc1 = pc1.createDataChannel('channel');
542 assert_equals(dc1.id, null, 'Expect initial id to be null');
543
544 const offer = await pc1.createOffer();
545 await Promise.all([pc1.setLocalDescription(offer), pc2.setRemoteDescription(offer)]);
546 const answer = await pc2.createAnswer();
547 await pc1.setRemoteDescription(answer);
548
549 assert_not_equals(dc1.id, null,
550 'Expect dc1.id to be assigned after remote description has been set');
551
552 assert_greater_than_equal(dc1.id, 0,
553 'Expect dc1.id to be set to valid unsigned short');
554
555 assert_less_than(dc1.id, 65535,
556 'Expect dc1.id to be set to valid unsigned short');
557
558 const dc2 = pc1.createDataChannel('channel');
559
560 assert_not_equals(dc2.id, null,
561 'Expect dc2.id to be assigned after remote description has been set');
562
563 assert_greater_than_equal(dc2.id, 0,
564 'Expect dc2.id to be set to valid unsigned short');
565
566 assert_less_than(dc2.id, 65535,
567 'Expect dc2.id to be set to valid unsigned short');
568
569 assert_not_equals(dc2, dc1,
570 'Expect channels created from same label to be different');
571
572 assert_equals(dc2.label, dc1.label,
573 'Expect different channels can have the same label but different id');
574
575 assert_not_equals(dc2.id, dc1.id,
576 'Expect different channels can have the same label but different id');
577
578 assert_equals(negotiatedDc.id, 42,
579 'Expect negotiatedDc.id to be 42 after remote description has been set');
580}, 'Channels created (after setRemoteDescription) should have id assigned');
581
Harald Alvestrandf6cf3bd2019-04-04 12:39:57582test(t => {
Soares Chen199b65f2017-06-21 09:20:39583 const pc = new RTCPeerConnection();
Philipp Hancke1622a022018-06-11 10:00:53584 t.add_cleanup(() => pc.close());
Soares Chen199b65f2017-06-21 09:20:39585
youennf0e97d092019-03-23 18:02:56586 const dc1 = pc.createDataChannel('channel-1', {
587 negotiated: true,
588 id: 42,
Soares Chen199b65f2017-06-21 09:20:39589 });
youennf0e97d092019-03-23 18:02:56590 assert_equals(dc1.id, 42,
591 'Expect dc1.id to be 42');
592
593 const dc2 = pc.createDataChannel('channel-2', {
594 negotiated: true,
595 id: 43,
596 });
597 assert_equals(dc2.id, 43,
598 'Expect dc2.id to be 43');
599
Stephen McGruerd5103042020-01-23 21:45:45600 assert_throws_dom('OperationError', () =>
youennf0e97d092019-03-23 18:02:56601 pc.createDataChannel('channel-3', {
602 negotiated: true,
603 id: 42,
604 }));
605
606}, 'Reusing a data channel id that is in use should throw OperationError');
607
608// We've seen implementations behaving differently before and after the connection has been
609// established.
Harald Alvestrandf6cf3bd2019-04-04 12:39:57610promise_test(async t => {
youennf0e97d092019-03-23 18:02:56611 const pc1 = new RTCPeerConnection();
612 const pc2 = new RTCPeerConnection();
613 t.add_cleanup(() => pc1.close());
614 t.add_cleanup(() => pc2.close());
615
616 const dc1 = pc1.createDataChannel('channel-1', {
617 negotiated: true,
618 id: 42,
619 });
620 assert_equals(dc1.id, 42, 'Expect dc1.id to be 42');
621
622 const dc2 = pc1.createDataChannel('channel-2', {
623 negotiated: true,
624 id: 43,
625 });
626 assert_equals(dc2.id, 43, 'Expect dc2.id to be 43');
627
628 const offer = await pc1.createOffer();
629 await Promise.all([pc1.setLocalDescription(offer), pc2.setRemoteDescription(offer)]);
630 const answer = await pc2.createAnswer();
631 await pc1.setRemoteDescription(answer);
632
633 assert_equals(dc1.id, 42, 'Expect dc1.id to be 42');
634
635 assert_equals(dc2.id, 43, 'Expect dc2.id to be 43');
636
Stephen McGruerd5103042020-01-23 21:45:45637 assert_throws_dom('OperationError', () =>
youennf0e97d092019-03-23 18:02:56638 pc1.createDataChannel('channel-3', {
639 negotiated: true,
640 id: 42,
641 }));
642}, 'Reusing a data channel id that is in use (after setRemoteDescription) should throw ' +
643 'OperationError');
644
Harald Alvestrandf6cf3bd2019-04-04 12:39:57645promise_test(async t => {
youennf0e97d092019-03-23 18:02:56646 const pc1 = new RTCPeerConnection();
647 const pc2 = new RTCPeerConnection();
648 t.add_cleanup(() => pc1.close());
649 t.add_cleanup(() => pc2.close());
650
651 const dc1 = pc1.createDataChannel('channel-1');
652
653 const offer = await pc1.createOffer();
654 await Promise.all([pc1.setLocalDescription(offer), pc2.setRemoteDescription(offer)]);
655 const answer = await pc2.createAnswer();
656 await pc1.setRemoteDescription(answer);
657
658 assert_not_equals(dc1.id, null,
659 'Expect dc1.id to be assigned after remote description has been set');
660
Stephen McGruerd5103042020-01-23 21:45:45661 assert_throws_dom('OperationError', () =>
youennf0e97d092019-03-23 18:02:56662 pc1.createDataChannel('channel-2', {
663 negotiated: true,
664 id: dc1.id,
665 }));
666}, 'Reusing a data channel id that is in use (after setRemoteDescription, negotiated via DCEP) ' +
667 'should throw OperationError');
668
669// Based on https://bugzilla.mozilla.org/show_bug.cgi?id=1441723
Harald Alvestrandf6cf3bd2019-04-04 12:39:57670promise_test(async t => {
youennf0e97d092019-03-23 18:02:56671 const pc1 = new RTCPeerConnection();
672 const pc2 = new RTCPeerConnection();
673 t.add_cleanup(() => pc1.close());
674 t.add_cleanup(() => pc2.close());
675
676 await createDataChannelPair(pc1, pc2);
677
678 const dc = pc1.createDataChannel('');
679 assert_equals(dc.readyState, 'connecting', 'Channel should be in the connecting state');
680}, 'New data channel should be in the connecting state after creation (after connection ' +
681 'establishment)');
Soares Chen199b65f2017-06-21 09:20:39682
moz-wptsync-botb0d2aa52019-10-02 14:44:10683promise_test(async t => {
684 const pc1 = new RTCPeerConnection();
685 const pc2 = new RTCPeerConnection();
686 t.add_cleanup(() => pc1.close());
687 t.add_cleanup(() => pc2.close());
688 const stream = await getNoiseStream({audio: true, video: true});
689 t.add_cleanup(() => stopTracks(stream));
690 const audio = stream.getAudioTracks()[0];
691 const video = stream.getVideoTracks()[0];
692 pc1.addTrack(audio, stream);
693 pc1.addTrack(video, stream);
694 await createDataChannelPair(pc1, pc2);
695}, 'addTrack, then createDataChannel, should negotiate properly');
696
697promise_test(async t => {
698 const pc1 = new RTCPeerConnection({bundlePolicy: "max-bundle"});
699 const pc2 = new RTCPeerConnection();
700 t.add_cleanup(() => pc1.close());
701 t.add_cleanup(() => pc2.close());
702 const stream = await getNoiseStream({audio: true, video: true});
703 t.add_cleanup(() => stopTracks(stream));
704 const audio = stream.getAudioTracks()[0];
705 const video = stream.getVideoTracks()[0];
706 pc1.addTrack(audio, stream);
707 pc1.addTrack(video, stream);
708 await createDataChannelPair(pc1, pc2);
709}, 'addTrack, then createDataChannel, should negotiate properly when max-bundle is used');
710
711promise_test(async t => {
712 const pc1 = new RTCPeerConnection({bundlePolicy: "max-bundle"});
713 const pc2 = new RTCPeerConnection();
714 t.add_cleanup(() => pc1.close());
715 t.add_cleanup(() => pc2.close());
716 const stream = await getNoiseStream({audio: true, video: true});
717 t.add_cleanup(() => stopTracks(stream));
718 const audio = stream.getAudioTracks()[0];
719 const video = stream.getVideoTracks()[0];
720 pc1.addTrack(audio, stream);
721 pc1.addTrack(video, stream);
722 const [dc1, dc2] = await createDataChannelPair(pc1, pc2);
723
724 pc2.getTransceivers()[0].stop();
725 const dc1Closed = new Promise(r => dc1.onclose = r);
726 await doSignalingHandshake(pc1, pc2);
727 await dc1Closed;
728}, 'Stopping the bundle-tag when there is a DataChannel in the bundle should kill the DataChannel');
729
Soares Chen199b65f2017-06-21 09:20:39730/*
Soares Chen6a9b7812017-06-08 13:25:47731 Untestable
Soares Chen199b65f2017-06-21 09:20:39732 6.1. createDataChannel
youennf0e97d092019-03-23 18:02:56733 19. If a setting, either [[MaxPacketLifeTime]] or [[MaxRetransmits]], has been set to
734 indicate unreliable mode, and that value exceeds the maximum value supported
735 by the user agent, the value MUST be set to the user agents maximum value.
Soares Chen199b65f2017-06-21 09:20:39736
youennf0e97d092019-03-23 18:02:56737 23. Return channel and continue the following steps in parallel.
738 24. Create channel's associated underlying data transport and configure
Soares Chen199b65f2017-06-21 09:20:39739 it according to the relevant properties of channel.
740
741 Tested in RTCPeerConnection-onnegotiationneeded.html
youennf0e97d092019-03-23 18:02:56742 22. If channel is the first RTCDataChannel created on connection, update the
743 negotiation-needed flag for connection.
Soares Chen199b65f2017-06-21 09:20:39744
youennf0e97d092019-03-23 18:02:56745 Tested in RTCDataChannel-id.html
746 - Odd/even rules for '.id'
Soares Chen199b65f2017-06-21 09:20:39747
youennf0e97d092019-03-23 18:02:56748 Tested in RTCDataChannel-dcep.html
749 - Transmission of '.label' and further options
Soares Chen199b65f2017-06-21 09:20:39750*/
foolip72525e92017-04-24 09:31:03751</script>