tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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>