blob: 943e5728775494b8c5be4c521ce9671cd4e1bc4e [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;
37 readonly attribute RTCPriorityType priority;
38 readonly attribute RTCDataChannelState readyState;
39 readonly attribute unsigned long bufferedAmount;
40 attribute unsigned long bufferedAmountLowThreshold;
41 [...]
42 attribute DOMString binaryType;
43 [...]
Soares Chen199b65f2017-06-21 09:20:3944 };
45
Soares Chen6a9b7812017-06-08 13:25:4746 dictionary RTCDataChannelInit {
youennf0e97d092019-03-23 18:02:5647 boolean ordered = true;
48 unsigned short maxPacketLifeTime;
49 unsigned short maxRetransmits;
50 USVString protocol = "";
51 boolean negotiated = false;
52 [EnforceRange]
53 unsigned short id;
54 RTCPriorityType priority = "low";
Soares Chen6a9b7812017-06-08 13:25:4755 };
Soares Chen199b65f2017-06-21 09:20:3956
57 4.9.1. RTCPriorityType Enum
58
59 enum RTCPriorityType {
60 "very-low",
61 "low",
62 "medium",
63 "high"
64 };
Soares Chen6a9b7812017-06-08 13:25:4765 */
foolip72525e92017-04-24 09:31:0366
Harald Alvestrandf6cf3bd2019-04-04 12:39:5767test(t => {
Soares Chen6a9b7812017-06-08 13:25:4768 const pc = new RTCPeerConnection();
youennf0e97d092019-03-23 18:02:5669 t.add_cleanup(() => pc.close());
70
Soares Chen6a9b7812017-06-08 13:25:4771 assert_equals(pc.createDataChannel.length, 1);
Boris Zbarsky71bf0202020-01-21 04:26:3772 assert_throws_js(TypeError, () => pc.createDataChannel());
Soares Chen6a9b7812017-06-08 13:25:4773}, 'createDataChannel with no argument should throw TypeError');
74
75/*
76 6.2. createDataChannel
77 2. If connection's [[isClosed]] slot is true, throw an InvalidStateError.
78 */
Harald Alvestrandf6cf3bd2019-04-04 12:39:5779test(t => {
Soares Chen6a9b7812017-06-08 13:25:4780 const pc = new RTCPeerConnection();
foolip72525e92017-04-24 09:31:0381 pc.close();
82 assert_equals(pc.signalingState, 'closed', 'signaling state');
83 assert_throws('InvalidStateError', () => pc.createDataChannel(''));
Soares Chen6a9b7812017-06-08 13:25:4784}, 'createDataChannel with closed connection should throw InvalidStateError');
foolip72525e92017-04-24 09:31:0385
Soares Chen6a9b7812017-06-08 13:25:4786/*
Soares Chen199b65f2017-06-21 09:20:3987 6.1. createDataChannel
youennf0e97d092019-03-23 18:02:5688 4. Let channel have a [[DataChannelLabel]] internal slot initialized to the value of the
Soares Chen199b65f2017-06-21 09:20:3989 first argument.
youennf0e97d092019-03-23 18:02:5690 6. Let options be the second argument.
91 7. Let channel have an [[MaxPacketLifeTime]] internal slot initialized to
Soares Chen199b65f2017-06-21 09:20:3992 option's maxPacketLifeTime member, if present, otherwise null.
youennf0e97d092019-03-23 18:02:5693 8. Let channel have a [[ReadyState]] internal slot initialized to "connecting".
94 9. Let channel have a [[BufferedAmount]] internal slot initialized to 0.
95 10. Let channel have an [[MaxRetransmits]] internal slot initialized to
Soares Chen199b65f2017-06-21 09:20:3996 option's maxRetransmits member, if present, otherwise null.
youennf0e97d092019-03-23 18:02:5697 11. Let channel have an [[Ordered]] internal slot initialized to option's
Soares Chen199b65f2017-06-21 09:20:3998 ordered member.
youennf0e97d092019-03-23 18:02:5699 12. Let channel have a [[DataChannelProtocol]] internal slot initialized to option's
Soares Chen199b65f2017-06-21 09:20:39100 protocol member.
youennf0e97d092019-03-23 18:02:56101 14. Let channel have a [[Negotiated]] internal slot initialized to option's negotiated
102 member.
103 15. Let channel have an [[DataChannelId]] internal slot initialized to option's id
104 member, if it is present and [[Negotiated]] is true, otherwise null.
105 17. Let channel have an [[DataChannelPriority]] internal slot initialized to option's
106 priority member.
107 21. If the [[DataChannelId]] slot is null (due to no ID being passed into
108 createDataChannel, or [[Negotiated]] being false), and the DTLS role of the SCTP
109 transport has already been negotiated, then initialize [[DataChannelId]] to a value
110 generated by the user agent, according to [RTCWEB-DATA-PROTOCOL], and skip
111 to the next step. If no available ID could be generated, or if the value of the
112 [[DataChannelId]] slot is being used by an existing RTCDataChannel, throw an
113 OperationError exception.
114
115 Note
116 If the [[DataChannelId]] slot is null after this step, it will be populated once
117 the DTLS role is determined during the process of setting an RTCSessionDescription.
118 22. If channel is the first RTCDataChannel created on connection, update the
119 negotiation-needed flag for connection.
120
Soares Chen199b65f2017-06-21 09:20:39121
122 6.2. RTCDataChannel
123
124 A RTCDataChannel, created with createDataChannel or dispatched via a
125 RTCDataChannelEvent, MUST initially be in the connecting state
126
youennf0e97d092019-03-23 18:02:56127 bufferedAmountLowThreshold
128 [...] The bufferedAmountLowThreshold is initially zero on each new RTCDataChannel,
129 but the application may change its value at any time.
130
Soares Chen199b65f2017-06-21 09:20:39131 binaryType
youennf0e97d092019-03-23 18:02:56132 [...] When a RTCDataChannel object is created, the binaryType attribute MUST
Soares Chen199b65f2017-06-21 09:20:39133 be initialized to the string "blob".
Soares Chen6a9b7812017-06-08 13:25:47134 */
Harald Alvestrandf6cf3bd2019-04-04 12:39:57135test(t => {
Soares Chen6a9b7812017-06-08 13:25:47136 const pc = new RTCPeerConnection();
youennf0e97d092019-03-23 18:02:56137 t.add_cleanup(() => pc.close());
Soares Chen199b65f2017-06-21 09:20:39138
youennf0e97d092019-03-23 18:02:56139 const dc = pc.createDataChannel('');
140
141 assert_true(dc instanceof RTCDataChannel, 'is RTCDataChannel');
142 assert_equals(dc.label, '');
143 assert_equals(dc.ordered, true);
144 assert_equals(dc.maxPacketLifeTime, null);
145 assert_equals(dc.maxRetransmits, null);
146 assert_equals(dc.protocol, '');
147 assert_equals(dc.negotiated, false);
Taylor Brandstetterce56f0e2017-05-09 13:53:14148 // Since no offer/answer exchange has occurred yet, the DTLS role is unknown
149 // and so the ID should be null.
youennf0e97d092019-03-23 18:02:56150 assert_equals(dc.id, null);
151 assert_equals(dc.priority, 'low');
152 assert_equals(dc.readyState, 'connecting');
153 assert_equals(dc.bufferedAmount, 0);
154 assert_equals(dc.bufferedAmountLowThreshold, 0);
155 assert_equals(dc.binaryType, 'blob');
Soares Chen6a9b7812017-06-08 13:25:47156}, 'createDataChannel attribute default values');
foolip72525e92017-04-24 09:31:03157
Harald Alvestrandf6cf3bd2019-04-04 12:39:57158test(t => {
Soares Chen199b65f2017-06-21 09:20:39159 const pc = new RTCPeerConnection();
youennf0e97d092019-03-23 18:02:56160 t.add_cleanup(() => pc.close());
161
162 const dc = pc.createDataChannel('test', {
Soares Chen199b65f2017-06-21 09:20:39163 ordered: false,
Soares Chen199b65f2017-06-21 09:20:39164 maxRetransmits: 1,
Harald Alvestrandf6cf3bd2019-04-04 12:39:57165 // Note: maxPacketLifeTime is not set in this test.
Soares Chen199b65f2017-06-21 09:20:39166 protocol: 'custom',
167 negotiated: true,
168 id: 3,
169 priority: 'high'
170 });
171
youennf0e97d092019-03-23 18:02:56172 assert_true(dc instanceof RTCDataChannel, 'is RTCDataChannel');
173 assert_equals(dc.label, 'test');
174 assert_equals(dc.ordered, false);
175 assert_equals(dc.maxPacketLifeTime, null);
176 assert_equals(dc.maxRetransmits, 1);
177 assert_equals(dc.protocol, 'custom');
178 assert_equals(dc.negotiated, true);
179 assert_equals(dc.id, 3);
180 assert_equals(dc.priority, 'high');
181 assert_equals(dc.readyState, 'connecting');
182 assert_equals(dc.bufferedAmount, 0);
183 assert_equals(dc.bufferedAmountLowThreshold, 0);
184 assert_equals(dc.binaryType, 'blob');
Harald Alvestrandf6cf3bd2019-04-04 12:39:57185
186 const dc2 = pc.createDataChannel('test2', {
187 ordered: false,
188 maxPacketLifeTime: 42
189 });
190 assert_equals(dc2.label, 'test2');
191 assert_equals(dc2.maxPacketLifeTime, 42);
192 assert_equals(dc.maxRetransmits, null);
Soares Chen199b65f2017-06-21 09:20:39193}, 'createDataChannel with provided parameters should initialize attributes to provided values');
Soares Chen6a9b7812017-06-08 13:25:47194
195/*
196 6.2. createDataChannel
youennf0e97d092019-03-23 18:02:56197 4. Let channel have a [[DataChannelLabel]] internal slot initialized to the value of the
Soares Chen199b65f2017-06-21 09:20:39198 first argument.
Soares Chen6a9b7812017-06-08 13:25:47199
200 [ECMA262] 7.1.12. ToString(argument)
201 undefined -> "undefined"
202 null -> "null"
203
204 [WebIDL] 3.10.15. Convert a DOMString to a sequence of Unicode scalar values
205 */
foolip72525e92017-04-24 09:31:03206const labels = [
207 ['"foo"', 'foo', 'foo'],
208 ['null', null, 'null'],
209 ['undefined', undefined, 'undefined'],
210 ['lone surrogate', '\uD800', '\uFFFD'],
211];
212for (const [description, label, expected] of labels) {
Harald Alvestrandf6cf3bd2019-04-04 12:39:57213 test(t => {
foolip72525e92017-04-24 09:31:03214 const pc = new RTCPeerConnection;
youennf0e97d092019-03-23 18:02:56215 t.add_cleanup(() => pc.close());
216
217 const dc = pc.createDataChannel(label);
218 assert_equals(dc.label, expected);
Soares Chen6a9b7812017-06-08 13:25:47219 }, `createDataChannel with label ${description} should succeed`);
foolip72525e92017-04-24 09:31:03220}
221
Soares Chen6a9b7812017-06-08 13:25:47222/*
223 6.2. RTCDataChannel
Soares Chen199b65f2017-06-21 09:20:39224 createDataChannel
youennf0e97d092019-03-23 18:02:56225 11. Let channel have an [[Ordered]] internal slot initialized to option's
Soares Chen199b65f2017-06-21 09:20:39226 ordered member.
Soares Chen6a9b7812017-06-08 13:25:47227 */
Harald Alvestrandf6cf3bd2019-04-04 12:39:57228test(t => {
Soares Chen6a9b7812017-06-08 13:25:47229 const pc = new RTCPeerConnection();
youennf0e97d092019-03-23 18:02:56230 t.add_cleanup(() => pc.close());
231
232 const dc = pc.createDataChannel('', { ordered: false });
233 assert_equals(dc.ordered, false);
Soares Chen6a9b7812017-06-08 13:25:47234}, 'createDataChannel with ordered false should succeed');
foolip72525e92017-04-24 09:31:03235
236// true as the default value of a boolean is confusing because null is converted
237// to false while undefined is converted to true.
Harald Alvestrandf6cf3bd2019-04-04 12:39:57238test(t => {
Soares Chen6a9b7812017-06-08 13:25:47239 const pc = new RTCPeerConnection();
youennf0e97d092019-03-23 18:02:56240 t.add_cleanup(() => pc.close());
241
242 const dc1 = pc.createDataChannel('', { ordered: null });
243 assert_equals(dc1.ordered, false);
244 const dc2 = pc.createDataChannel('', { ordered: undefined });
245 assert_equals(dc2.ordered, true);
Soares Chen6a9b7812017-06-08 13:25:47246}, 'createDataChannel with ordered null/undefined should succeed');
foolip72525e92017-04-24 09:31:03247
Soares Chen6a9b7812017-06-08 13:25:47248/*
249 6.2. RTCDataChannel
Soares Chen199b65f2017-06-21 09:20:39250 createDataChannel
youennf0e97d092019-03-23 18:02:56251 7. Let channel have an [[MaxPacketLifeTime]] internal slot initialized to
Soares Chen199b65f2017-06-21 09:20:39252 option's maxPacketLifeTime member, if present, otherwise null.
Soares Chen6a9b7812017-06-08 13:25:47253 */
Harald Alvestrandf6cf3bd2019-04-04 12:39:57254test(t => {
foolip72525e92017-04-24 09:31:03255 const pc = new RTCPeerConnection;
youennf0e97d092019-03-23 18:02:56256 t.add_cleanup(() => pc.close());
257
258 const dc = pc.createDataChannel('', { maxPacketLifeTime: 0 });
259 assert_equals(dc.maxPacketLifeTime, 0);
Soares Chen6a9b7812017-06-08 13:25:47260}, 'createDataChannel with maxPacketLifeTime 0 should succeed');
foolip72525e92017-04-24 09:31:03261
Soares Chen6a9b7812017-06-08 13:25:47262/*
263 6.2. RTCDataChannel
Soares Chen199b65f2017-06-21 09:20:39264 createDataChannel
youennf0e97d092019-03-23 18:02:56265 10. Let channel have an [[MaxRetransmits]] internal slot initialized to
Soares Chen199b65f2017-06-21 09:20:39266 option's maxRetransmits member, if present, otherwise null.
Soares Chen6a9b7812017-06-08 13:25:47267 */
Harald Alvestrandf6cf3bd2019-04-04 12:39:57268test(t => {
foolip72525e92017-04-24 09:31:03269 const pc = new RTCPeerConnection;
youennf0e97d092019-03-23 18:02:56270 t.add_cleanup(() => pc.close());
271
272 const dc = pc.createDataChannel('', { maxRetransmits: 0 });
273 assert_equals(dc.maxRetransmits, 0);
Soares Chen6a9b7812017-06-08 13:25:47274}, 'createDataChannel with maxRetransmits 0 should succeed');
foolip72525e92017-04-24 09:31:03275
Soares Chen6a9b7812017-06-08 13:25:47276/*
277 6.2. createDataChannel
youennf0e97d092019-03-23 18:02:56278 18. If both [[MaxPacketLifeTime]] and [[MaxRetransmits]] attributes are set (not null),
279 throw a TypeError.
Soares Chen6a9b7812017-06-08 13:25:47280 */
Harald Alvestrandf6cf3bd2019-04-04 12:39:57281test(t => {
foolip72525e92017-04-24 09:31:03282 const pc = new RTCPeerConnection;
youennf0e97d092019-03-23 18:02:56283 t.add_cleanup(() => pc.close());
284
285 pc.createDataChannel('', {
Harald Alvestrandf6cf3bd2019-04-04 12:39:57286 maxPacketLifeTime: undefined,
287 maxRetransmits: undefined
youennf0e97d092019-03-23 18:02:56288 });
Harald Alvestrandf6cf3bd2019-04-04 12:39:57289}, 'createDataChannel with both maxPacketLifeTime and maxRetransmits undefined should succeed');
youennf0e97d092019-03-23 18:02:56290
Harald Alvestrandf6cf3bd2019-04-04 12:39:57291test(t => {
youennf0e97d092019-03-23 18:02:56292 const pc = new RTCPeerConnection;
293 t.add_cleanup(() => pc.close());
294
Boris Zbarsky71bf0202020-01-21 04:26:37295 assert_throws_js(TypeError, () => pc.createDataChannel('', {
foolip72525e92017-04-24 09:31:03296 maxPacketLifeTime: 0,
297 maxRetransmits: 0
298 }));
Boris Zbarsky71bf0202020-01-21 04:26:37299 assert_throws_js(TypeError, () => pc.createDataChannel('', {
youennf0e97d092019-03-23 18:02:56300 maxPacketLifeTime: 42,
301 maxRetransmits: 42
302 }));
303}, 'createDataChannel with both maxPacketLifeTime and maxRetransmits should throw TypeError');
foolip72525e92017-04-24 09:31:03304
Soares Chen6a9b7812017-06-08 13:25:47305/*
306 6.2. RTCDataChannel
Soares Chen199b65f2017-06-21 09:20:39307 createDataChannel
youennf0e97d092019-03-23 18:02:56308 12. Let channel have a [[DataChannelProtocol]] internal slot initialized to option's
Soares Chen199b65f2017-06-21 09:20:39309 protocol member.
Soares Chen6a9b7812017-06-08 13:25:47310 */
foolip72525e92017-04-24 09:31:03311const protocols = [
312 ['"foo"', 'foo', 'foo'],
313 ['null', null, 'null'],
314 ['undefined', undefined, ''],
315 ['lone surrogate', '\uD800', '\uFFFD'],
316];
317for (const [description, protocol, expected] of protocols) {
Harald Alvestrandf6cf3bd2019-04-04 12:39:57318 test(t => {
foolip72525e92017-04-24 09:31:03319 const pc = new RTCPeerConnection;
youennf0e97d092019-03-23 18:02:56320 t.add_cleanup(() => pc.close());
321
322 const dc = pc.createDataChannel('', { protocol });
323 assert_equals(dc.protocol, expected);
Soares Chen6a9b7812017-06-08 13:25:47324 }, `createDataChannel with protocol ${description} should succeed`);
foolip72525e92017-04-24 09:31:03325}
326
Soares Chen6a9b7812017-06-08 13:25:47327/*
328 6.2. RTCDataChannel
Soares Chen199b65f2017-06-21 09:20:39329 createDataChannel
youennf0e97d092019-03-23 18:02:56330 20. If [[DataChannelId]] is equal to 65535, which is greater than the maximum allowed
331 ID of 65534 but still qualifies as an unsigned short, throw a TypeError.
Soares Chen6a9b7812017-06-08 13:25:47332 */
Boris Zbarskyf1d2a712020-01-21 04:24:46333for (const id of [0, 1, 65534, 65535]) {
334 test((t) => {
335 const pc = new RTCPeerConnection();
336 t.add_cleanup(() => pc.close());
337 const dc = pc.createDataChannel('', { id });
338 assert_equals(dc.id, null);
339 }, `createDataChannel with id ${id} and negotiated not set should succeed, but not set the channel's id`);
340}
341
Soares Chen6a9b7812017-06-08 13:25:47342for (const id of [0, 1, 65534]) {
Harald Alvestrandf6cf3bd2019-04-04 12:39:57343 test(t => {
Soares Chen6a9b7812017-06-08 13:25:47344 const pc = new RTCPeerConnection();
youennf0e97d092019-03-23 18:02:56345 t.add_cleanup(() => pc.close());
346
Harald Alvestrand267d5dd2019-04-02 12:40:16347 const dc = pc.createDataChannel('', { 'negotiated': true, 'id': id });
youennf0e97d092019-03-23 18:02:56348 assert_equals(dc.id, id);
Boris Zbarskyf1d2a712020-01-21 04:24:46349 }, `createDataChannel with id ${id} and negotiated true should succeed, and set the channel's id`);
350}
351
352for (const id of [-1, 65536]) {
353 test((t) => {
354 const pc = new RTCPeerConnection();
355 t.add_cleanup(() => pc.close());
Boris Zbarsky71bf0202020-01-21 04:26:37356 assert_throws_js(TypeError, () => pc.createDataChannel('', { id }));
Boris Zbarskyf1d2a712020-01-21 04:24:46357 }, `createDataChannel with id ${id} and negotiated not set should throw TypeError`);
foolip72525e92017-04-24 09:31:03358}
359
Soares Chen6a9b7812017-06-08 13:25:47360for (const id of [-1, 65535, 65536]) {
Harald Alvestrandf6cf3bd2019-04-04 12:39:57361 test(t => {
Soares Chen6a9b7812017-06-08 13:25:47362 const pc = new RTCPeerConnection();
youennf0e97d092019-03-23 18:02:56363 t.add_cleanup(() => pc.close());
364
Boris Zbarsky71bf0202020-01-21 04:26:37365 assert_throws_js(TypeError, () => pc.createDataChannel('',
Harald Alvestrand267d5dd2019-04-02 12:40:16366 { 'negotiated': true, 'id': id }));
Soares Chen6a9b7812017-06-08 13:25:47367 }, `createDataChannel with id ${id} should throw TypeError`);
368}
369
370/*
371 6.2. RTCDataChannel
Soares Chen199b65f2017-06-21 09:20:39372 createDataChannel
youennf0e97d092019-03-23 18:02:56373 17. Let channel have an [[DataChannelPriority]] internal slot initialized to option's
374 priority member.
Soares Chen6a9b7812017-06-08 13:25:47375 */
Harald Alvestrandf6cf3bd2019-04-04 12:39:57376test(t => {
Soares Chen6a9b7812017-06-08 13:25:47377 const pc = new RTCPeerConnection();
youennf0e97d092019-03-23 18:02:56378 t.add_cleanup(() => pc.close());
379
380 const dc = pc.createDataChannel('', { priority: 'high' });
381 assert_equals(dc.priority, 'high');
Soares Chen6a9b7812017-06-08 13:25:47382}, 'createDataChannel with priority "high" should succeed');
383
Harald Alvestrandf6cf3bd2019-04-04 12:39:57384test(t => {
Soares Chen6a9b7812017-06-08 13:25:47385 const pc = new RTCPeerConnection();
youennf0e97d092019-03-23 18:02:56386 t.add_cleanup(() => pc.close());
387
Soares Chen6a9b7812017-06-08 13:25:47388 assert_throws(new TypeError(),
389 () => pc.createDataChannel('', { priority: 'invalid' }));
390}, 'createDataChannel with invalid priority should throw TypeError');
391
392/*
393 6.2. createDataChannel
youennf0e97d092019-03-23 18:02:56394 5. If [[DataChannelLabel]] is longer than 65535 bytes, throw a TypeError.
395 */
Harald Alvestrandf6cf3bd2019-04-04 12:39:57396test(t => {
Soares Chen6a9b7812017-06-08 13:25:47397 const pc = new RTCPeerConnection();
youennf0e97d092019-03-23 18:02:56398 t.add_cleanup(() => pc.close());
399
Boris Zbarsky71bf0202020-01-21 04:26:37400 assert_throws_js(TypeError, () =>
youennf0e97d092019-03-23 18:02:56401 pc.createDataChannel('l'.repeat(65536)));
402
Boris Zbarsky71bf0202020-01-21 04:26:37403 assert_throws_js(TypeError, () =>
youennf0e97d092019-03-23 18:02:56404 pc.createDataChannel('l'.repeat(65536), {
405 negotiated: true,
406 id: 42
Soares Chen6a9b7812017-06-08 13:25:47407 }));
youennf0e97d092019-03-23 18:02:56408}, 'createDataChannel with too long label should throw TypeError');
409
Harald Alvestrandf6cf3bd2019-04-04 12:39:57410test(t => {
youennf0e97d092019-03-23 18:02:56411 const pc = new RTCPeerConnection();
412 t.add_cleanup(() => pc.close());
413
Boris Zbarsky71bf0202020-01-21 04:26:37414 assert_throws_js(TypeError, () =>
youennf0e97d092019-03-23 18:02:56415 pc.createDataChannel('\u00b5'.repeat(32768)));
416
Boris Zbarsky71bf0202020-01-21 04:26:37417 assert_throws_js(TypeError, () =>
youennf0e97d092019-03-23 18:02:56418 pc.createDataChannel('\u00b5'.repeat(32768), {
419 negotiated: true,
420 id: 42
421 }));
422}, 'createDataChannel with too long label (2 byte unicode) should throw TypeError');
423
424/*
425 6.2. label
426 [...] Scripts are allowed to create multiple RTCDataChannel objects with the same label.
427 [...]
428 */
429test(t => {
430 const pc = new RTCPeerConnection();
431 t.add_cleanup(() => pc.close());
432
433 const label = 'test';
434
435 pc.createDataChannel(label);
436 pc.createDataChannel(label);
437}, 'createDataChannel with same label used twice should not throw');
Soares Chen6a9b7812017-06-08 13:25:47438
439/*
440 6.2. createDataChannel
youennf0e97d092019-03-23 18:02:56441 13. If [[DataChannelProtocol]] is longer than 65535 bytes long, throw a TypeError.
Soares Chen6a9b7812017-06-08 13:25:47442 */
youennf0e97d092019-03-23 18:02:56443
Harald Alvestrandf6cf3bd2019-04-04 12:39:57444test(t => {
youennf0e97d092019-03-23 18:02:56445 const pc = new RTCPeerConnection;
Harald Alvestrandf6cf3bd2019-04-04 12:39:57446 t.add_cleanup(() => pc.close());
447 const channel = pc.createDataChannel('', { negotiated: true, id: 42 });
youennf0e97d092019-03-23 18:02:56448 assert_equals(channel.negotiated, true);
Harald Alvestrandf6cf3bd2019-04-04 12:39:57449}, 'createDataChannel with negotiated true and id should succeed');
youennf0e97d092019-03-23 18:02:56450
Harald Alvestrandf6cf3bd2019-04-04 12:39:57451test(t => {
Soares Chen6a9b7812017-06-08 13:25:47452 const pc = new RTCPeerConnection();
youennf0e97d092019-03-23 18:02:56453 t.add_cleanup(() => pc.close());
454
Boris Zbarsky71bf0202020-01-21 04:26:37455 assert_throws_js(TypeError, () =>
Soares Chen6a9b7812017-06-08 13:25:47456 pc.createDataChannel('', {
youennf0e97d092019-03-23 18:02:56457 protocol: 'p'.repeat(65536)
Soares Chen6a9b7812017-06-08 13:25:47458 }));
Soares Chen6a9b7812017-06-08 13:25:47459
Boris Zbarsky71bf0202020-01-21 04:26:37460 assert_throws_js(TypeError, () =>
youennf0e97d092019-03-23 18:02:56461 pc.createDataChannel('', {
462 protocol: 'p'.repeat(65536),
463 negotiated: true,
464 id: 42
465 }));
466}, 'createDataChannel with too long protocol should throw TypeError');
467
Harald Alvestrandf6cf3bd2019-04-04 12:39:57468test(t => {
Soares Chen6a9b7812017-06-08 13:25:47469 const pc = new RTCPeerConnection();
youennf0e97d092019-03-23 18:02:56470 t.add_cleanup(() => pc.close());
Soares Chen199b65f2017-06-21 09:20:39471
Boris Zbarsky71bf0202020-01-21 04:26:37472 assert_throws_js(TypeError, () =>
youennf0e97d092019-03-23 18:02:56473 pc.createDataChannel('', {
474 protocol: '\u00b6'.repeat(32768)
475 }));
476
Boris Zbarsky71bf0202020-01-21 04:26:37477 assert_throws_js(TypeError, () =>
youennf0e97d092019-03-23 18:02:56478 pc.createDataChannel('', {
479 protocol: '\u00b6'.repeat(32768),
480 negotiated: true,
481 id: 42
482 }));
483}, 'createDataChannel with too long protocol (2 byte unicode) should throw TypeError');
484
Harald Alvestrandf6cf3bd2019-04-04 12:39:57485test(t => {
youennf0e97d092019-03-23 18:02:56486 const pc = new RTCPeerConnection();
487 t.add_cleanup(() => pc.close());
488
489 const label = 'l'.repeat(65535);
490 const protocol = 'p'.repeat(65535);
491
492 const dc = pc.createDataChannel(label, {
493 protocol: protocol
Soares Chen6a9b7812017-06-08 13:25:47494 });
Soares Chen199b65f2017-06-21 09:20:39495
youennf0e97d092019-03-23 18:02:56496 assert_equals(dc.label, label);
497 assert_equals(dc.protocol, protocol);
498}, 'createDataChannel with maximum length label and protocol should succeed');
499
500/*
501 6.2 createDataChannel
502 15. Let channel have an [[DataChannelId]] internal slot initialized to option's id member,
503 if it is present and [[Negotiated]] is true, otherwise null.
504
505 NOTE
506 This means the id member will be ignored if the data channel is negotiated in-band; this
507 is intentional. Data channels negotiated in-band should have IDs selected based on the
508 DTLS role, as specified in [RTCWEB-DATA-PROTOCOL].
509 */
Harald Alvestrandf6cf3bd2019-04-04 12:39:57510test(t => {
youennf0e97d092019-03-23 18:02:56511 const pc = new RTCPeerConnection;
512 t.add_cleanup(() => pc.close());
513
514 const dc = pc.createDataChannel('', {
515 negotiated: false,
516 });
517 assert_equals(dc.negotiated, false, 'Expect dc.negotiated to be false');
518}, 'createDataChannel with negotiated false should succeed');
519
Harald Alvestrandf6cf3bd2019-04-04 12:39:57520test(t => {
youennf0e97d092019-03-23 18:02:56521 const pc = new RTCPeerConnection;
522 t.add_cleanup(() => pc.close());
523
524 const dc = pc.createDataChannel('', {
525 negotiated: false,
526 id: 42
527 });
528 assert_equals(dc.negotiated, false, 'Expect dc.negotiated to be false');
529 assert_equals(dc.id, null, 'Expect dc.id to be ignored (null)');
530}, 'createDataChannel with negotiated false and id 42 should ignore the id');
531
532/*
533 6.2. createDataChannel
534 16. If [[Negotiated]] is true and [[DataChannelId]] is null, throw a TypeError.
535 */
Harald Alvestrandf6cf3bd2019-04-04 12:39:57536test(t => {
youennf0e97d092019-03-23 18:02:56537 const pc = new RTCPeerConnection();
538 t.add_cleanup(() => pc.close());
539
Boris Zbarsky71bf0202020-01-21 04:26:37540 assert_throws_js(TypeError, () =>
youennf0e97d092019-03-23 18:02:56541 pc.createDataChannel('test', {
542 negotiated: true
543 }));
544}, 'createDataChannel with negotiated true and id not defined should throw TypeError');
545
Soares Chen6a9b7812017-06-08 13:25:47546/*
Soares Chen199b65f2017-06-21 09:20:39547 4.4.1.6. Set the RTCSessionSessionDescription
548 2.2.6. If description is of type "answer" or "pranswer", then run the
549 following steps:
youennf0e97d092019-03-23 18:02:56550 3. If description negotiates the DTLS role of the SCTP transport, and there is an
551 RTCDataChannel with a null id, then generate an ID according to
552 [RTCWEB-DATA-PROTOCOL]. [...]
Soares Chen199b65f2017-06-21 09:20:39553
youennf0e97d092019-03-23 18:02:56554 6.1. createDataChannel
555 21. If the [[DataChannelId]] slot is null (due to no ID being passed into
556 createDataChannel, or [[Negotiated]] being false), and the DTLS role of the SCTP
557 transport has already been negotiated, then initialize [[DataChannelId]] to a value
558 generated by the user agent, according to [RTCWEB-DATA-PROTOCOL], and skip
559 to the next step. If no available ID could be generated, or if the value of the
560 [[DataChannelId]] slot is being used by an existing RTCDataChannel, throw an
561 OperationError exception.
562
563 Note
564 If the [[DataChannelId]] slot is null after this step, it will be populated once
565 the DTLS role is determined during the process of setting an RTCSessionDescription.
Soares Chen199b65f2017-06-21 09:20:39566 */
Harald Alvestrandf6cf3bd2019-04-04 12:39:57567promise_test(async t => {
youennf0e97d092019-03-23 18:02:56568 const pc1 = new RTCPeerConnection();
569 const pc2 = new RTCPeerConnection();
570 t.add_cleanup(() => pc1.close());
571 t.add_cleanup(() => pc2.close());
572
573 const negotiatedDc = pc1.createDataChannel('negotiated-channel', {
574 negotiated: true,
575 id: 42,
576 });
577 assert_equals(negotiatedDc.id, 42, 'Expect negotiatedDc.id to be 42');
578
579 const dc1 = pc1.createDataChannel('channel');
580 assert_equals(dc1.id, null, 'Expect initial id to be null');
581
582 const offer = await pc1.createOffer();
583 await Promise.all([pc1.setLocalDescription(offer), pc2.setRemoteDescription(offer)]);
584 const answer = await pc2.createAnswer();
585 await pc1.setRemoteDescription(answer);
586
587 assert_not_equals(dc1.id, null,
588 'Expect dc1.id to be assigned after remote description has been set');
589
590 assert_greater_than_equal(dc1.id, 0,
591 'Expect dc1.id to be set to valid unsigned short');
592
593 assert_less_than(dc1.id, 65535,
594 'Expect dc1.id to be set to valid unsigned short');
595
596 const dc2 = pc1.createDataChannel('channel');
597
598 assert_not_equals(dc2.id, null,
599 'Expect dc2.id to be assigned after remote description has been set');
600
601 assert_greater_than_equal(dc2.id, 0,
602 'Expect dc2.id to be set to valid unsigned short');
603
604 assert_less_than(dc2.id, 65535,
605 'Expect dc2.id to be set to valid unsigned short');
606
607 assert_not_equals(dc2, dc1,
608 'Expect channels created from same label to be different');
609
610 assert_equals(dc2.label, dc1.label,
611 'Expect different channels can have the same label but different id');
612
613 assert_not_equals(dc2.id, dc1.id,
614 'Expect different channels can have the same label but different id');
615
616 assert_equals(negotiatedDc.id, 42,
617 'Expect negotiatedDc.id to be 42 after remote description has been set');
618}, 'Channels created (after setRemoteDescription) should have id assigned');
619
Harald Alvestrandf6cf3bd2019-04-04 12:39:57620test(t => {
Soares Chen199b65f2017-06-21 09:20:39621 const pc = new RTCPeerConnection();
Philipp Hancke1622a022018-06-11 10:00:53622 t.add_cleanup(() => pc.close());
Soares Chen199b65f2017-06-21 09:20:39623
youennf0e97d092019-03-23 18:02:56624 const dc1 = pc.createDataChannel('channel-1', {
625 negotiated: true,
626 id: 42,
Soares Chen199b65f2017-06-21 09:20:39627 });
youennf0e97d092019-03-23 18:02:56628 assert_equals(dc1.id, 42,
629 'Expect dc1.id to be 42');
630
631 const dc2 = pc.createDataChannel('channel-2', {
632 negotiated: true,
633 id: 43,
634 });
635 assert_equals(dc2.id, 43,
636 'Expect dc2.id to be 43');
637
638 assert_throws('OperationError', () =>
639 pc.createDataChannel('channel-3', {
640 negotiated: true,
641 id: 42,
642 }));
643
644}, 'Reusing a data channel id that is in use should throw OperationError');
645
646// We've seen implementations behaving differently before and after the connection has been
647// established.
Harald Alvestrandf6cf3bd2019-04-04 12:39:57648promise_test(async t => {
youennf0e97d092019-03-23 18:02:56649 const pc1 = new RTCPeerConnection();
650 const pc2 = new RTCPeerConnection();
651 t.add_cleanup(() => pc1.close());
652 t.add_cleanup(() => pc2.close());
653
654 const dc1 = pc1.createDataChannel('channel-1', {
655 negotiated: true,
656 id: 42,
657 });
658 assert_equals(dc1.id, 42, 'Expect dc1.id to be 42');
659
660 const dc2 = pc1.createDataChannel('channel-2', {
661 negotiated: true,
662 id: 43,
663 });
664 assert_equals(dc2.id, 43, 'Expect dc2.id to be 43');
665
666 const offer = await pc1.createOffer();
667 await Promise.all([pc1.setLocalDescription(offer), pc2.setRemoteDescription(offer)]);
668 const answer = await pc2.createAnswer();
669 await pc1.setRemoteDescription(answer);
670
671 assert_equals(dc1.id, 42, 'Expect dc1.id to be 42');
672
673 assert_equals(dc2.id, 43, 'Expect dc2.id to be 43');
674
675 assert_throws('OperationError', () =>
676 pc1.createDataChannel('channel-3', {
677 negotiated: true,
678 id: 42,
679 }));
680}, 'Reusing a data channel id that is in use (after setRemoteDescription) should throw ' +
681 'OperationError');
682
Harald Alvestrandf6cf3bd2019-04-04 12:39:57683promise_test(async t => {
youennf0e97d092019-03-23 18:02:56684 const pc1 = new RTCPeerConnection();
685 const pc2 = new RTCPeerConnection();
686 t.add_cleanup(() => pc1.close());
687 t.add_cleanup(() => pc2.close());
688
689 const dc1 = pc1.createDataChannel('channel-1');
690
691 const offer = await pc1.createOffer();
692 await Promise.all([pc1.setLocalDescription(offer), pc2.setRemoteDescription(offer)]);
693 const answer = await pc2.createAnswer();
694 await pc1.setRemoteDescription(answer);
695
696 assert_not_equals(dc1.id, null,
697 'Expect dc1.id to be assigned after remote description has been set');
698
699 assert_throws('OperationError', () =>
700 pc1.createDataChannel('channel-2', {
701 negotiated: true,
702 id: dc1.id,
703 }));
704}, 'Reusing a data channel id that is in use (after setRemoteDescription, negotiated via DCEP) ' +
705 'should throw OperationError');
706
707// Based on https://bugzilla.mozilla.org/show_bug.cgi?id=1441723
Harald Alvestrandf6cf3bd2019-04-04 12:39:57708promise_test(async t => {
youennf0e97d092019-03-23 18:02:56709 const pc1 = new RTCPeerConnection();
710 const pc2 = new RTCPeerConnection();
711 t.add_cleanup(() => pc1.close());
712 t.add_cleanup(() => pc2.close());
713
714 await createDataChannelPair(pc1, pc2);
715
716 const dc = pc1.createDataChannel('');
717 assert_equals(dc.readyState, 'connecting', 'Channel should be in the connecting state');
718}, 'New data channel should be in the connecting state after creation (after connection ' +
719 'establishment)');
Soares Chen199b65f2017-06-21 09:20:39720
moz-wptsync-botb0d2aa52019-10-02 14:44:10721promise_test(async t => {
722 const pc1 = new RTCPeerConnection();
723 const pc2 = new RTCPeerConnection();
724 t.add_cleanup(() => pc1.close());
725 t.add_cleanup(() => pc2.close());
726 const stream = await getNoiseStream({audio: true, video: true});
727 t.add_cleanup(() => stopTracks(stream));
728 const audio = stream.getAudioTracks()[0];
729 const video = stream.getVideoTracks()[0];
730 pc1.addTrack(audio, stream);
731 pc1.addTrack(video, stream);
732 await createDataChannelPair(pc1, pc2);
733}, 'addTrack, then createDataChannel, should negotiate properly');
734
735promise_test(async t => {
736 const pc1 = new RTCPeerConnection({bundlePolicy: "max-bundle"});
737 const pc2 = new RTCPeerConnection();
738 t.add_cleanup(() => pc1.close());
739 t.add_cleanup(() => pc2.close());
740 const stream = await getNoiseStream({audio: true, video: true});
741 t.add_cleanup(() => stopTracks(stream));
742 const audio = stream.getAudioTracks()[0];
743 const video = stream.getVideoTracks()[0];
744 pc1.addTrack(audio, stream);
745 pc1.addTrack(video, stream);
746 await createDataChannelPair(pc1, pc2);
747}, 'addTrack, then createDataChannel, should negotiate properly when max-bundle is used');
748
749promise_test(async t => {
750 const pc1 = new RTCPeerConnection({bundlePolicy: "max-bundle"});
751 const pc2 = new RTCPeerConnection();
752 t.add_cleanup(() => pc1.close());
753 t.add_cleanup(() => pc2.close());
754 const stream = await getNoiseStream({audio: true, video: true});
755 t.add_cleanup(() => stopTracks(stream));
756 const audio = stream.getAudioTracks()[0];
757 const video = stream.getVideoTracks()[0];
758 pc1.addTrack(audio, stream);
759 pc1.addTrack(video, stream);
760 const [dc1, dc2] = await createDataChannelPair(pc1, pc2);
761
762 pc2.getTransceivers()[0].stop();
763 const dc1Closed = new Promise(r => dc1.onclose = r);
764 await doSignalingHandshake(pc1, pc2);
765 await dc1Closed;
766}, 'Stopping the bundle-tag when there is a DataChannel in the bundle should kill the DataChannel');
767
Soares Chen199b65f2017-06-21 09:20:39768/*
Soares Chen6a9b7812017-06-08 13:25:47769 Untestable
Soares Chen199b65f2017-06-21 09:20:39770 6.1. createDataChannel
youennf0e97d092019-03-23 18:02:56771 19. If a setting, either [[MaxPacketLifeTime]] or [[MaxRetransmits]], has been set to
772 indicate unreliable mode, and that value exceeds the maximum value supported
773 by the user agent, the value MUST be set to the user agents maximum value.
Soares Chen199b65f2017-06-21 09:20:39774
youennf0e97d092019-03-23 18:02:56775 23. Return channel and continue the following steps in parallel.
776 24. Create channel's associated underlying data transport and configure
Soares Chen199b65f2017-06-21 09:20:39777 it according to the relevant properties of channel.
778
779 Tested in RTCPeerConnection-onnegotiationneeded.html
youennf0e97d092019-03-23 18:02:56780 22. If channel is the first RTCDataChannel created on connection, update the
781 negotiation-needed flag for connection.
Soares Chen199b65f2017-06-21 09:20:39782
youennf0e97d092019-03-23 18:02:56783 Tested in RTCDataChannel-id.html
784 - Odd/even rules for '.id'
Soares Chen199b65f2017-06-21 09:20:39785
youennf0e97d092019-03-23 18:02:56786 Tested in RTCDataChannel-dcep.html
787 - Transmission of '.label' and further options
Soares Chen199b65f2017-06-21 09:20:39788*/
foolip72525e92017-04-24 09:31:03789</script>