RTCPeerConnection-createDataChannel.html (25913B)
1 <!doctype html> 2 <meta charset=utf-8> 3 <meta name="timeout" content="long"> 4 <title>RTCPeerConnection.prototype.createDataChannel</title> 5 <script src=/resources/testharness.js></script> 6 <script src=/resources/testharnessreport.js></script> 7 <script src="RTCPeerConnection-helper.js"></script> 8 <script> 9 'use strict'; 10 11 const stopTracks = (...streams) => { 12 streams.forEach(stream => stream.getTracks().forEach(track => track.stop())); 13 }; 14 15 // Test is based on the following revision: 16 // https://rawgit.com/w3c/webrtc-pc/1cc5bfc3ff18741033d804c4a71f7891242fb5b3/webrtc.html 17 18 /* 19 6.1. RTCPeerConnection Interface Extensions 20 21 partial interface RTCPeerConnection { 22 [...] 23 RTCDataChannel createDataChannel(USVString label, 24 optional RTCDataChannelInit dataChannelDict); 25 [...] 26 }; 27 28 6.2. RTCDataChannel 29 30 interface RTCDataChannel : EventTarget { 31 readonly attribute USVString label; 32 readonly attribute boolean ordered; 33 readonly attribute unsigned short? maxPacketLifeTime; 34 readonly attribute unsigned short? maxRetransmits; 35 readonly attribute USVString protocol; 36 readonly attribute boolean negotiated; 37 readonly attribute unsigned short? id; 38 readonly attribute RTCDataChannelState readyState; 39 readonly attribute unsigned long bufferedAmount; 40 attribute unsigned long bufferedAmountLowThreshold; 41 [...] 42 attribute DOMString binaryType; 43 [...] 44 }; 45 46 dictionary RTCDataChannelInit { 47 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 }; 55 */ 56 57 test(t => { 58 const pc = new RTCPeerConnection(); 59 t.add_cleanup(() => pc.close()); 60 61 assert_equals(pc.createDataChannel.length, 1); 62 assert_throws_js(TypeError, () => pc.createDataChannel()); 63 }, 'createDataChannel with no argument should throw TypeError'); 64 65 /* 66 6.2. createDataChannel 67 2. If connection's [[isClosed]] slot is true, throw an InvalidStateError. 68 */ 69 test(t => { 70 const pc = new RTCPeerConnection(); 71 pc.close(); 72 assert_equals(pc.signalingState, 'closed', 'signaling state'); 73 assert_throws_dom('InvalidStateError', () => pc.createDataChannel('')); 74 }, 'createDataChannel with closed connection should throw InvalidStateError'); 75 76 /* 77 6.1. createDataChannel 78 4. Let channel have a [[DataChannelLabel]] internal slot initialized to the value of the 79 first argument. 80 6. Let options be the second argument. 81 7. Let channel have an [[MaxPacketLifeTime]] internal slot initialized to 82 option's maxPacketLifeTime member, if present, otherwise null. 83 8. Let channel have a [[ReadyState]] internal slot initialized to "connecting". 84 9. Let channel have a [[BufferedAmount]] internal slot initialized to 0. 85 10. Let channel have an [[MaxRetransmits]] internal slot initialized to 86 option's maxRetransmits member, if present, otherwise null. 87 11. Let channel have an [[Ordered]] internal slot initialized to option's 88 ordered member. 89 12. Let channel have a [[DataChannelProtocol]] internal slot initialized to option's 90 protocol member. 91 14. Let channel have a [[Negotiated]] internal slot initialized to option's negotiated 92 member. 93 15. Let channel have an [[DataChannelId]] internal slot initialized to option's id 94 member, if it is present and [[Negotiated]] is true, otherwise null. 95 21. If the [[DataChannelId]] slot is null (due to no ID being passed into 96 createDataChannel, or [[Negotiated]] being false), and the DTLS role of the SCTP 97 transport has already been negotiated, then initialize [[DataChannelId]] to a value 98 generated by the user agent, according to [RTCWEB-DATA-PROTOCOL], and skip 99 to the next step. If no available ID could be generated, or if the value of the 100 [[DataChannelId]] slot is being used by an existing RTCDataChannel, throw an 101 OperationError exception. 102 103 Note 104 If the [[DataChannelId]] slot is null after this step, it will be populated once 105 the DTLS role is determined during the process of setting an RTCSessionDescription. 106 22. If channel is the first RTCDataChannel created on connection, update the 107 negotiation-needed flag for connection. 108 109 110 6.2. RTCDataChannel 111 112 A RTCDataChannel, created with createDataChannel or dispatched via a 113 RTCDataChannelEvent, MUST initially be in the connecting state 114 115 bufferedAmountLowThreshold 116 [...] The bufferedAmountLowThreshold is initially zero on each new RTCDataChannel, 117 but the application may change its value at any time. 118 119 binaryType 120 [...] When a RTCDataChannel object is created, the binaryType attribute MUST 121 be initialized to the string "blob". 122 */ 123 test(t => { 124 const pc = new RTCPeerConnection(); 125 t.add_cleanup(() => pc.close()); 126 127 const dc = pc.createDataChannel(''); 128 129 assert_true(dc instanceof RTCDataChannel, 'is RTCDataChannel'); 130 assert_equals(dc.label, ''); 131 assert_equals(dc.ordered, true); 132 assert_equals(dc.maxPacketLifeTime, null); 133 assert_equals(dc.maxRetransmits, null); 134 assert_equals(dc.protocol, ''); 135 assert_equals(dc.negotiated, false); 136 // Since no offer/answer exchange has occurred yet, the DTLS role is unknown 137 // and so the ID should be null. 138 assert_equals(dc.id, null); 139 assert_equals(dc.readyState, 'connecting'); 140 assert_equals(dc.bufferedAmount, 0); 141 assert_equals(dc.bufferedAmountLowThreshold, 0); 142 assert_equals(dc.binaryType, 'arraybuffer'); 143 }, 'createDataChannel attribute default values'); 144 145 test(t => { 146 const pc = new RTCPeerConnection(); 147 t.add_cleanup(() => pc.close()); 148 149 const dc = pc.createDataChannel('test', { 150 ordered: false, 151 maxRetransmits: 1, 152 // Note: maxPacketLifeTime is not set in this test. 153 protocol: 'custom', 154 negotiated: true, 155 id: 3 156 }); 157 158 assert_true(dc instanceof RTCDataChannel, 'is RTCDataChannel'); 159 assert_equals(dc.label, 'test'); 160 assert_equals(dc.ordered, false); 161 assert_equals(dc.maxPacketLifeTime, null); 162 assert_equals(dc.maxRetransmits, 1); 163 assert_equals(dc.protocol, 'custom'); 164 assert_equals(dc.negotiated, true); 165 assert_equals(dc.id, 3); 166 assert_equals(dc.readyState, 'connecting'); 167 assert_equals(dc.bufferedAmount, 0); 168 assert_equals(dc.bufferedAmountLowThreshold, 0); 169 assert_equals(dc.binaryType, 'arraybuffer'); 170 171 const dc2 = pc.createDataChannel('test2', { 172 ordered: false, 173 maxPacketLifeTime: 42 174 }); 175 assert_equals(dc2.label, 'test2'); 176 assert_equals(dc2.maxPacketLifeTime, 42); 177 assert_equals(dc2.maxRetransmits, null); 178 }, 'createDataChannel with provided parameters should initialize attributes to provided values'); 179 180 /* 181 6.2. createDataChannel 182 4. Let channel have a [[DataChannelLabel]] internal slot initialized to the value of the 183 first argument. 184 185 [ECMA262] 7.1.12. ToString(argument) 186 undefined -> "undefined" 187 null -> "null" 188 189 [WebIDL] 3.10.15. Convert a DOMString to a sequence of Unicode scalar values 190 */ 191 const labels = [ 192 ['"foo"', 'foo', 'foo'], 193 ['null', null, 'null'], 194 ['undefined', undefined, 'undefined'], 195 ['lone surrogate', '\uD800', '\uFFFD'], 196 ]; 197 for (const [description, label, expected] of labels) { 198 test(t => { 199 const pc = new RTCPeerConnection; 200 t.add_cleanup(() => pc.close()); 201 202 const dc = pc.createDataChannel(label); 203 assert_equals(dc.label, expected); 204 }, `createDataChannel with label ${description} should succeed`); 205 } 206 207 /* 208 6.2. RTCDataChannel 209 createDataChannel 210 11. Let channel have an [[Ordered]] internal slot initialized to option's 211 ordered member. 212 */ 213 test(t => { 214 const pc = new RTCPeerConnection(); 215 t.add_cleanup(() => pc.close()); 216 217 const dc = pc.createDataChannel('', { ordered: false }); 218 assert_equals(dc.ordered, false); 219 }, 'createDataChannel with ordered false should succeed'); 220 221 // true as the default value of a boolean is confusing because null is converted 222 // to false while undefined is converted to true. 223 test(t => { 224 const pc = new RTCPeerConnection(); 225 t.add_cleanup(() => pc.close()); 226 227 const dc1 = pc.createDataChannel('', { ordered: null }); 228 assert_equals(dc1.ordered, false); 229 const dc2 = pc.createDataChannel('', { ordered: undefined }); 230 assert_equals(dc2.ordered, true); 231 }, 'createDataChannel with ordered null/undefined should succeed'); 232 233 /* 234 6.2. RTCDataChannel 235 createDataChannel 236 7. Let channel have an [[MaxPacketLifeTime]] internal slot initialized to 237 option's maxPacketLifeTime member, if present, otherwise null. 238 */ 239 test(t => { 240 const pc = new RTCPeerConnection; 241 t.add_cleanup(() => pc.close()); 242 243 const dc = pc.createDataChannel('', { maxPacketLifeTime: 0 }); 244 assert_equals(dc.maxPacketLifeTime, 0); 245 }, 'createDataChannel with maxPacketLifeTime 0 should succeed'); 246 247 /* 248 6.2. RTCDataChannel 249 createDataChannel 250 10. Let channel have an [[MaxRetransmits]] internal slot initialized to 251 option's maxRetransmits member, if present, otherwise null. 252 */ 253 test(t => { 254 const pc = new RTCPeerConnection; 255 t.add_cleanup(() => pc.close()); 256 257 const dc = pc.createDataChannel('', { maxRetransmits: 0 }); 258 assert_equals(dc.maxRetransmits, 0); 259 }, 'createDataChannel with maxRetransmits 0 should succeed'); 260 261 /* 262 6.2. createDataChannel 263 18. If both [[MaxPacketLifeTime]] and [[MaxRetransmits]] attributes are set (not null), 264 throw a TypeError. 265 */ 266 test(t => { 267 const pc = new RTCPeerConnection; 268 t.add_cleanup(() => pc.close()); 269 270 pc.createDataChannel('', { 271 maxPacketLifeTime: undefined, 272 maxRetransmits: undefined 273 }); 274 }, 'createDataChannel with both maxPacketLifeTime and maxRetransmits undefined should succeed'); 275 276 test(t => { 277 const pc = new RTCPeerConnection; 278 t.add_cleanup(() => pc.close()); 279 280 assert_throws_js(TypeError, () => pc.createDataChannel('', { 281 maxPacketLifeTime: 0, 282 maxRetransmits: 0 283 })); 284 assert_throws_js(TypeError, () => pc.createDataChannel('', { 285 maxPacketLifeTime: 42, 286 maxRetransmits: 42 287 })); 288 }, 'createDataChannel with both maxPacketLifeTime and maxRetransmits should throw TypeError'); 289 290 /* 291 6.2. RTCDataChannel 292 createDataChannel 293 12. Let channel have a [[DataChannelProtocol]] internal slot initialized to option's 294 protocol member. 295 */ 296 const protocols = [ 297 ['"foo"', 'foo', 'foo'], 298 ['null', null, 'null'], 299 ['undefined', undefined, ''], 300 ['lone surrogate', '\uD800', '\uFFFD'], 301 ]; 302 for (const [description, protocol, expected] of protocols) { 303 test(t => { 304 const pc = new RTCPeerConnection; 305 t.add_cleanup(() => pc.close()); 306 307 const dc = pc.createDataChannel('', { protocol }); 308 assert_equals(dc.protocol, expected); 309 }, `createDataChannel with protocol ${description} should succeed`); 310 } 311 312 /* 313 6.2. RTCDataChannel 314 createDataChannel 315 20. If [[DataChannelId]] is equal to 65535, which is greater than the maximum allowed 316 ID of 65534 but still qualifies as an unsigned short, throw a TypeError. 317 */ 318 for (const id of [0, 1, 65534, 65535]) { 319 test((t) => { 320 const pc = new RTCPeerConnection(); 321 t.add_cleanup(() => pc.close()); 322 const dc = pc.createDataChannel('', { id }); 323 assert_equals(dc.id, null); 324 }, `createDataChannel with id ${id} and negotiated not set should succeed, but not set the channel's id`); 325 } 326 327 for (const id of [0, 1, 65534]) { 328 test(t => { 329 const pc = new RTCPeerConnection(); 330 t.add_cleanup(() => pc.close()); 331 332 const dc = pc.createDataChannel('', { 'negotiated': true, 'id': id }); 333 assert_equals(dc.id, id); 334 }, `createDataChannel with id ${id} and negotiated true should succeed, and set the channel's id`); 335 } 336 337 for (const id of [-1, 65536]) { 338 test((t) => { 339 const pc = new RTCPeerConnection(); 340 t.add_cleanup(() => pc.close()); 341 assert_throws_js(TypeError, () => pc.createDataChannel('', { id })); 342 }, `createDataChannel with id ${id} and negotiated not set should throw TypeError`); 343 } 344 345 for (const id of [-1, 65535, 65536]) { 346 test(t => { 347 const pc = new RTCPeerConnection(); 348 t.add_cleanup(() => pc.close()); 349 350 assert_throws_js(TypeError, () => pc.createDataChannel('', 351 { 'negotiated': true, 'id': id })); 352 }, `createDataChannel with id ${id} should throw TypeError`); 353 } 354 355 /* 356 6.2. createDataChannel 357 5. If [[DataChannelLabel]] is longer than 65535 bytes, throw a TypeError. 358 */ 359 test(t => { 360 const pc = new RTCPeerConnection(); 361 t.add_cleanup(() => pc.close()); 362 363 assert_throws_js(TypeError, () => 364 pc.createDataChannel('l'.repeat(65536))); 365 366 assert_throws_js(TypeError, () => 367 pc.createDataChannel('l'.repeat(65536), { 368 negotiated: true, 369 id: 42 370 })); 371 }, 'createDataChannel with too long label should throw TypeError'); 372 373 test(t => { 374 const pc = new RTCPeerConnection(); 375 t.add_cleanup(() => pc.close()); 376 377 assert_throws_js(TypeError, () => 378 pc.createDataChannel('\u00b5'.repeat(32768))); 379 380 assert_throws_js(TypeError, () => 381 pc.createDataChannel('\u00b5'.repeat(32768), { 382 negotiated: true, 383 id: 42 384 })); 385 }, 'createDataChannel with too long label (2 byte unicode) should throw TypeError'); 386 387 /* 388 6.2. label 389 [...] Scripts are allowed to create multiple RTCDataChannel objects with the same label. 390 [...] 391 */ 392 test(t => { 393 const pc = new RTCPeerConnection(); 394 t.add_cleanup(() => pc.close()); 395 396 const label = 'test'; 397 398 pc.createDataChannel(label); 399 pc.createDataChannel(label); 400 }, 'createDataChannel with same label used twice should not throw'); 401 402 /* 403 6.2. createDataChannel 404 13. If [[DataChannelProtocol]] is longer than 65535 bytes long, throw a TypeError. 405 */ 406 407 test(t => { 408 const pc = new RTCPeerConnection; 409 t.add_cleanup(() => pc.close()); 410 const channel = pc.createDataChannel('', { negotiated: true, id: 42 }); 411 assert_equals(channel.negotiated, true); 412 }, 'createDataChannel with negotiated true and id should succeed'); 413 414 test(t => { 415 const pc = new RTCPeerConnection(); 416 t.add_cleanup(() => pc.close()); 417 418 assert_throws_js(TypeError, () => 419 pc.createDataChannel('', { 420 protocol: 'p'.repeat(65536) 421 })); 422 423 assert_throws_js(TypeError, () => 424 pc.createDataChannel('', { 425 protocol: 'p'.repeat(65536), 426 negotiated: true, 427 id: 42 428 })); 429 }, 'createDataChannel with too long protocol should throw TypeError'); 430 431 test(t => { 432 const pc = new RTCPeerConnection(); 433 t.add_cleanup(() => pc.close()); 434 435 assert_throws_js(TypeError, () => 436 pc.createDataChannel('', { 437 protocol: '\u00b6'.repeat(32768) 438 })); 439 440 assert_throws_js(TypeError, () => 441 pc.createDataChannel('', { 442 protocol: '\u00b6'.repeat(32768), 443 negotiated: true, 444 id: 42 445 })); 446 }, 'createDataChannel with too long protocol (2 byte unicode) should throw TypeError'); 447 448 test(t => { 449 const pc = new RTCPeerConnection(); 450 t.add_cleanup(() => pc.close()); 451 452 const label = 'l'.repeat(65535); 453 const protocol = 'p'.repeat(65535); 454 455 const dc = pc.createDataChannel(label, { 456 protocol: protocol 457 }); 458 459 assert_equals(dc.label, label); 460 assert_equals(dc.protocol, protocol); 461 }, 'createDataChannel with maximum length label and protocol should succeed'); 462 463 /* 464 6.2 createDataChannel 465 15. Let channel have an [[DataChannelId]] internal slot initialized to option's id member, 466 if it is present and [[Negotiated]] is true, otherwise null. 467 468 NOTE 469 This means the id member will be ignored if the data channel is negotiated in-band; this 470 is intentional. Data channels negotiated in-band should have IDs selected based on the 471 DTLS role, as specified in [RTCWEB-DATA-PROTOCOL]. 472 */ 473 test(t => { 474 const pc = new RTCPeerConnection; 475 t.add_cleanup(() => pc.close()); 476 477 const dc = pc.createDataChannel('', { 478 negotiated: false, 479 }); 480 assert_equals(dc.negotiated, false, 'Expect dc.negotiated to be false'); 481 }, 'createDataChannel with negotiated false should succeed'); 482 483 test(t => { 484 const pc = new RTCPeerConnection; 485 t.add_cleanup(() => pc.close()); 486 487 const dc = pc.createDataChannel('', { 488 negotiated: false, 489 id: 42 490 }); 491 assert_equals(dc.negotiated, false, 'Expect dc.negotiated to be false'); 492 assert_equals(dc.id, null, 'Expect dc.id to be ignored (null)'); 493 }, 'createDataChannel with negotiated false and id 42 should ignore the id'); 494 495 /* 496 6.2. createDataChannel 497 16. If [[Negotiated]] is true and [[DataChannelId]] is null, throw a TypeError. 498 */ 499 test(t => { 500 const pc = new RTCPeerConnection(); 501 t.add_cleanup(() => pc.close()); 502 503 assert_throws_js(TypeError, () => 504 pc.createDataChannel('test', { 505 negotiated: true 506 })); 507 }, 'createDataChannel with negotiated true and id not defined should throw TypeError'); 508 509 /* 510 The ID is assigned after SCTP connects, according to section 6.1.1.3 (RTCSctpTransport 511 Connected procedure). 512 */ 513 promise_test(async t => { 514 const pc1 = new RTCPeerConnection(); 515 const pc2 = new RTCPeerConnection(); 516 t.add_cleanup(() => pc1.close()); 517 t.add_cleanup(() => pc2.close()); 518 exchangeIceCandidates(pc1, pc2); 519 520 const negotiatedDc = pc1.createDataChannel('negotiated-channel', { 521 negotiated: true, 522 id: 42, 523 }); 524 assert_equals(negotiatedDc.id, 42, 'Expect negotiatedDc.id to be 42'); 525 526 const dc1 = pc1.createDataChannel('channel'); 527 assert_equals(dc1.id, null, 'Expect initial id to be null'); 528 529 const offer = await pc1.createOffer(); 530 await Promise.all([pc1.setLocalDescription(offer), pc2.setRemoteDescription(offer)]); 531 const answer = await pc2.createAnswer(); 532 await Promise.all([pc1.setRemoteDescription(answer), pc2.setLocalDescription(answer)]); 533 await waitForState(pc1.sctp, 'connected'); 534 await waitForState(pc2.sctp, 'connected'); 535 536 assert_not_equals(dc1.id, null, 537 'Expect dc1.id to be assigned after remote description has been set'); 538 539 assert_greater_than_equal(dc1.id, 0, 540 'Expect dc1.id to be set to valid unsigned short'); 541 542 assert_less_than(dc1.id, 65535, 543 'Expect dc1.id to be set to valid unsigned short'); 544 545 const dc2 = pc1.createDataChannel('channel'); 546 547 assert_not_equals(dc2.id, null, 548 'Expect dc2.id to be assigned after remote description has been set'); 549 550 assert_greater_than_equal(dc2.id, 0, 551 'Expect dc2.id to be set to valid unsigned short'); 552 553 assert_less_than(dc2.id, 65535, 554 'Expect dc2.id to be set to valid unsigned short'); 555 556 assert_not_equals(dc2, dc1, 557 'Expect channels created from same label to be different'); 558 559 assert_equals(dc2.label, dc1.label, 560 'Expect different channels can have the same label but different id'); 561 562 assert_not_equals(dc2.id, dc1.id, 563 'Expect different channels can have the same label but different id'); 564 565 assert_equals(negotiatedDc.id, 42, 566 'Expect negotiatedDc.id to be 42 after remote description has been set'); 567 }, 'Channels created (after SCTP connected) should have id assigned'); 568 569 test(t => { 570 const pc = new RTCPeerConnection(); 571 t.add_cleanup(() => pc.close()); 572 573 const dc1 = pc.createDataChannel('channel-1', { 574 negotiated: true, 575 id: 42, 576 }); 577 assert_equals(dc1.id, 42, 578 'Expect dc1.id to be 42'); 579 580 const dc2 = pc.createDataChannel('channel-2', { 581 negotiated: true, 582 id: 43, 583 }); 584 assert_equals(dc2.id, 43, 585 'Expect dc2.id to be 43'); 586 587 assert_throws_dom('OperationError', () => 588 pc.createDataChannel('channel-3', { 589 negotiated: true, 590 id: 42, 591 })); 592 593 }, 'Reusing a data channel id that is in use should throw OperationError'); 594 595 // We've seen implementations behaving differently before and after the connection has been 596 // established. 597 promise_test(async t => { 598 const pc1 = new RTCPeerConnection(); 599 const pc2 = new RTCPeerConnection(); 600 t.add_cleanup(() => pc1.close()); 601 t.add_cleanup(() => pc2.close()); 602 603 const dc1 = pc1.createDataChannel('channel-1', { 604 negotiated: true, 605 id: 42, 606 }); 607 assert_equals(dc1.id, 42, 'Expect dc1.id to be 42'); 608 609 const dc2 = pc1.createDataChannel('channel-2', { 610 negotiated: true, 611 id: 43, 612 }); 613 assert_equals(dc2.id, 43, 'Expect dc2.id to be 43'); 614 615 const offer = await pc1.createOffer(); 616 await Promise.all([pc1.setLocalDescription(offer), pc2.setRemoteDescription(offer)]); 617 const answer = await pc2.createAnswer(); 618 await pc1.setRemoteDescription(answer); 619 620 assert_equals(dc1.id, 42, 'Expect dc1.id to be 42'); 621 622 assert_equals(dc2.id, 43, 'Expect dc2.id to be 43'); 623 624 assert_throws_dom('OperationError', () => 625 pc1.createDataChannel('channel-3', { 626 negotiated: true, 627 id: 42, 628 })); 629 }, 'Reusing a data channel id that is in use (after setRemoteDescription) should throw ' + 630 'OperationError'); 631 632 promise_test(async t => { 633 const pc1 = new RTCPeerConnection(); 634 const pc2 = new RTCPeerConnection(); 635 t.add_cleanup(() => pc1.close()); 636 t.add_cleanup(() => pc2.close()); 637 exchangeIceCandidates(pc1, pc2); 638 639 const dc1 = pc1.createDataChannel('channel-1'); 640 641 const offer = await pc1.createOffer(); 642 await Promise.all([pc1.setLocalDescription(offer), pc2.setRemoteDescription(offer)]); 643 const answer = await pc2.createAnswer(); 644 await Promise.all([pc1.setRemoteDescription(answer), pc2.setLocalDescription(answer)]); 645 await waitForState(pc1.sctp, 'connected'); 646 await waitForState(pc2.sctp, 'connected'); 647 assert_not_equals(dc1.id, null, 648 'Expect dc1.id to be assigned after SCTP has connected'); 649 650 assert_throws_dom('OperationError', () => 651 pc1.createDataChannel('channel-2', { 652 negotiated: true, 653 id: dc1.id, 654 })); 655 }, 'Reusing a data channel id that is in use (after setRemoteDescription, negotiated via DCEP) ' + 656 'should throw OperationError'); 657 658 659 for (const options of [{}, {negotiated: true, id: 0}]) { 660 const mode = `${options.negotiated? "negotiated " : ""}datachannel`; 661 662 // Based on https://bugzilla.mozilla.org/show_bug.cgi?id=1441723 663 promise_test(async t => { 664 const pc1 = new RTCPeerConnection(); 665 t.add_cleanup(() => pc1.close()); 666 667 await createDataChannelPair(t, options, pc1); 668 669 const dc = pc1.createDataChannel(''); 670 assert_equals(dc.readyState, 'connecting', 'Channel should be in the connecting state'); 671 }, `New ${mode} should be in the connecting state after creation ` + 672 `(after connection establishment)`); 673 674 promise_test(async t => { 675 const pc1 = new RTCPeerConnection(); 676 t.add_cleanup(() => pc1.close()); 677 const stream = await getNoiseStream({audio: true, video: true}); 678 t.add_cleanup(() => stopTracks(stream)); 679 const audio = stream.getAudioTracks()[0]; 680 const video = stream.getVideoTracks()[0]; 681 pc1.addTrack(audio, stream); 682 pc1.addTrack(video, stream); 683 await createDataChannelPair(t, options, pc1); 684 }, `addTrack, then creating ${mode}, should negotiate properly`); 685 686 promise_test(async t => { 687 const pc1 = new RTCPeerConnection({bundlePolicy: "max-bundle"}); 688 t.add_cleanup(() => pc1.close()); 689 const stream = await getNoiseStream({audio: true, video: true}); 690 t.add_cleanup(() => stopTracks(stream)); 691 const audio = stream.getAudioTracks()[0]; 692 const video = stream.getVideoTracks()[0]; 693 pc1.addTrack(audio, stream); 694 pc1.addTrack(video, stream); 695 await createDataChannelPair(t, options, pc1); 696 }, `addTrack, then creating ${mode}, should negotiate properly when max-bundle is used`); 697 698 promise_test(async t => { 699 const pc1 = new RTCPeerConnection(); 700 const pc2 = new RTCPeerConnection(); 701 const stream = await getNoiseStream({audio: true, video: true}); 702 stream.getTracks().forEach((track) => pc1.addTrack(track, stream)); 703 await createDataChannelPair(t, options, pc1, pc2); 704 705 await pc1.setLocalDescription(); 706 await pc2.setRemoteDescription(pc1.localDescription); 707 await pc2.setLocalDescription(); 708 await pc1.setRemoteDescription(pc2.localDescription); 709 }, `Renegotiation with audio/video and ${mode} should work properly`); 710 711 /* 712 This test is disabled until https://github.com/w3c/webrtc-pc/issues/2562 713 has been resolved; it presupposes that stopping the first transceiver 714 breaks the transport. 715 716 promise_test(async t => { 717 const pc1 = new RTCPeerConnection({bundlePolicy: "max-bundle"}); 718 const pc2 = new RTCPeerConnection(); 719 t.add_cleanup(() => pc1.close()); 720 t.add_cleanup(() => pc2.close()); 721 const stream = await getNoiseStream({audio: true, video: true}); 722 t.add_cleanup(() => stopTracks(stream)); 723 const audio = stream.getAudioTracks()[0]; 724 const video = stream.getVideoTracks()[0]; 725 pc1.addTrack(audio, stream); 726 pc1.addTrack(video, stream); 727 const [dc1, dc2] = await createDataChannelPair(t, options, pc1, pc2); 728 729 pc2.getTransceivers()[0].stop(); 730 const dc1Closed = new Promise(r => dc1.onclose = r); 731 await exchangeOfferAnswer(pc1, pc2); 732 await dc1Closed; 733 }, `Stopping the bundle-tag when there is a ${mode} in the bundle ` + 734 `should kill the DataChannel`); 735 */ 736 } 737 738 /* 739 Untestable 740 6.1. createDataChannel 741 19. If a setting, either [[MaxPacketLifeTime]] or [[MaxRetransmits]], has been set to 742 indicate unreliable mode, and that value exceeds the maximum value supported 743 by the user agent, the value MUST be set to the user agents maximum value. 744 745 23. Return channel and continue the following steps in parallel. 746 24. Create channel's associated underlying data transport and configure 747 it according to the relevant properties of channel. 748 749 Tested in RTCPeerConnection-onnegotiationneeded.html 750 22. If channel is the first RTCDataChannel created on connection, update the 751 negotiation-needed flag for connection. 752 753 Tested in RTCDataChannel-id.html 754 - Odd/even rules for '.id' 755 756 Tested in RTCDataChannel-dcep.html 757 - Transmission of '.label' and further options 758 */ 759 </script>