tor-browser

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

RTCPeerConnection-ondatachannel.html (12587B)


      1 <!doctype html>
      2 <meta charset=utf-8>
      3 <meta name="timeout" content="long">
      4 <title>RTCPeerConnection.prototype.ondatachannel</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 // Test is based on the following revision:
     12 // https://rawgit.com/w3c/webrtc-pc/1cc5bfc3ff18741033d804c4a71f7891242fb5b3/webrtc.html
     13 
     14 // The following helper functions are called from RTCPeerConnection-helper.js:
     15 // exchangeIceCandidates
     16 // exchangeOfferAnswer
     17 // createDataChannelPair
     18 
     19 /*
     20  6.2.  RTCDataChannel
     21    When an underlying data transport is to be announced (the other peer created a channel with
     22    negotiated unset or set to false), the user agent of the peer that did not initiate the
     23    creation process MUST queue a task to run the following steps:
     24      2. Let channel be a newly created RTCDataChannel object.
     25      7. Set channel's [[ReadyState]] to open (but do not fire the open event, yet).
     26      8. Fire a datachannel event named datachannel with channel at the RTCPeerConnection object.
     27 
     28  6.3.  RTCDataChannelEvent
     29    Firing a datachannel event named e with an RTCDataChannel channel means that an event with the
     30    name e, which does not bubble (except where otherwise stated) and is not cancelable (except
     31    where otherwise stated), and which uses the RTCDataChannelEvent interface with the channel
     32    attribute set to channel, MUST be created and dispatched at the given target.
     33 
     34    interface RTCDataChannelEvent : Event {
     35      readonly attribute RTCDataChannel channel;
     36    };
     37 */
     38 promise_test(async (t) => {
     39  const resolver = new Resolver();
     40  const pc1 = new RTCPeerConnection();
     41  const pc2 = new RTCPeerConnection();
     42  t.add_cleanup(() => pc1.close());
     43  t.add_cleanup(() => pc2.close());
     44 
     45  let eventCount = 0;
     46 
     47  pc2.ondatachannel = t.step_func((event) => {
     48    eventCount++;
     49    assert_equals(eventCount, 1,
     50      'Expect data channel event to fire exactly once');
     51 
     52    assert_true(event instanceof RTCDataChannelEvent,
     53      'Expect event to be instance of RTCDataChannelEvent');
     54 
     55    assert_equals(event.bubbles, false);
     56    assert_equals(event.cancelable, false);
     57 
     58    const dc = event.channel;
     59    assert_true(dc instanceof RTCDataChannel,
     60      'Expect channel to be instance of RTCDataChannel');
     61 
     62    // The channel should be in the 'open' state already.
     63    // See: https://github.com/w3c/webrtc-pc/pull/1851
     64    assert_equals(dc.readyState, 'open',
     65      'Expect channel ready state to be open');
     66 
     67    resolver.resolve();
     68  });
     69 
     70  pc1.createDataChannel('fire-me!');
     71 
     72  exchangeIceCandidates(pc1, pc2);
     73  await exchangeOfferAnswer(pc1, pc2);
     74 
     75  await resolver;
     76 }, 'Data channel event should fire when new data channel is announced to the remote peer');
     77 
     78 /*
     79  Since the channel should be in the 'open' state when dispatching via the 'datachannel' event,
     80  we should be able to send data in the event handler.
     81 */
     82 promise_test(async (t) => {
     83  const resolver = new Resolver();
     84  const pc1 = new RTCPeerConnection();
     85  const pc2 = new RTCPeerConnection();
     86  t.add_cleanup(() => pc1.close());
     87  t.add_cleanup(() => pc2.close());
     88 
     89  const message = 'meow meow!';
     90 
     91  pc2.ondatachannel = t.step_func((event) => {
     92    const dc2 = event.channel;
     93    dc2.send(message);
     94  });
     95 
     96  const dc1 = pc1.createDataChannel('fire-me!');
     97  dc1.onmessage = t.step_func((event) => {
     98    assert_equals(event.data, message,
     99      'Received data should be equal to sent data');
    100 
    101    resolver.resolve();
    102  });
    103 
    104  exchangeIceCandidates(pc1, pc2);
    105  await exchangeOfferAnswer(pc1, pc2);
    106 
    107  await resolver;
    108 }, 'Should be able to send data in a datachannel event handler');
    109 
    110 /*
    111  6.2.  RTCDataChannel
    112    When an underlying data transport is to be announced (the other peer created a channel with
    113    negotiated unset or set to false), the user agent of the peer that did not initiate the
    114    creation process MUST queue a task to run the following steps:
    115      8. Fire a datachannel event named datachannel with channel at the RTCPeerConnection object.
    116      9. If the channel's [[ReadyState]] is still open, announce the data channel as open.
    117 */
    118 promise_test(async (t) => {
    119  const resolver = new Resolver();
    120  const pc1 = new RTCPeerConnection();
    121  const pc2 = new RTCPeerConnection();
    122  t.add_cleanup(() => pc1.close());
    123  t.add_cleanup(() => pc2.close());
    124 
    125  pc2.ondatachannel = t.step_func((event) => {
    126    const dc = event.channel;
    127    dc.onopen = t.step_func(() => {
    128      assert_unreached('Open event should not fire');
    129    });
    130 
    131    // This should prevent triggering the 'open' event
    132    dc.close();
    133 
    134    // Wait a bit to ensure the 'open' event does NOT fire
    135    t.step_timeout(() => resolver.resolve(), 500);
    136  });
    137 
    138  pc1.createDataChannel('fire-me!');
    139 
    140  exchangeIceCandidates(pc1, pc2);
    141  await exchangeOfferAnswer(pc1, pc2);
    142 
    143  await resolver;
    144 }, 'Open event should not be raised when closing the channel in the datachannel event');
    145 
    146 // Added this test as a result of the discussion in
    147 // https://github.com/w3c/webrtc-pc/pull/1851#discussion_r185976747
    148 promise_test(async (t) => {
    149  const resolver = new Resolver();
    150  const pc1 = new RTCPeerConnection();
    151  const pc2 = new RTCPeerConnection();
    152  t.add_cleanup(() => pc1.close());
    153  t.add_cleanup(() => pc2.close());
    154 
    155  pc2.ondatachannel = t.step_func((event) => {
    156    const dc = event.channel;
    157    dc.onopen = t.step_func((event) => {
    158      resolver.resolve();
    159    });
    160 
    161    // This should NOT prevent triggering the 'open' event since it enqueues at least two tasks
    162    t.step_timeout(() => {
    163      t.step_timeout(() => {
    164        dc.close()
    165      }, 1);
    166    }, 1);
    167  });
    168 
    169  pc1.createDataChannel('fire-me!');
    170 
    171  exchangeIceCandidates(pc1, pc2);
    172  await exchangeOfferAnswer(pc1, pc2);
    173 
    174  await resolver;
    175 }, 'Open event should be raised when closing the channel in the datachannel event after ' +
    176  'enqueuing a task');
    177 
    178 
    179 /*
    180  Combination of the two tests above (send and close).
    181 */
    182 promise_test(async (t) => {
    183  const resolver = new Resolver();
    184  const pc1 = new RTCPeerConnection();
    185  const pc2 = new RTCPeerConnection();
    186  t.add_cleanup(() => pc1.close());
    187  t.add_cleanup(() => pc2.close());
    188 
    189  const message = 'meow meow!';
    190 
    191  pc2.ondatachannel = t.step_func((event) => {
    192    const dc2 = event.channel;
    193    dc2.onopen = t.step_func(() => {
    194      assert_unreached('Open event should not fire');
    195    });
    196 
    197    // This should send but still prevent triggering the 'open' event
    198    dc2.send(message);
    199    dc2.close();
    200  });
    201 
    202  const dc1 = pc1.createDataChannel('fire-me!');
    203  dc1.onmessage = t.step_func((event) => {
    204    assert_equals(event.data, message,
    205      'Received data should be equal to sent data');
    206 
    207    resolver.resolve();
    208  });
    209 
    210  exchangeIceCandidates(pc1, pc2);
    211  await exchangeOfferAnswer(pc1, pc2);
    212 
    213  await resolver;
    214 }, 'Open event should not be raised when sending and immediately closing the channel in the ' +
    215  'datachannel event');
    216 
    217 /*
    218  6.2.  RTCDataChannel
    219 
    220    interface RTCDataChannel : EventTarget {
    221      readonly attribute USVString           label;
    222      readonly attribute boolean             ordered;
    223      readonly attribute unsigned short?     maxPacketLifeTime;
    224      readonly attribute unsigned short?     maxRetransmits;
    225      readonly attribute USVString           protocol;
    226      readonly attribute boolean             negotiated;
    227      readonly attribute unsigned short?     id;
    228      readonly attribute RTCDataChannelState readyState;
    229      ...
    230    };
    231 
    232    When an underlying data transport is to be announced (the other peer created a channel with
    233    negotiated unset or set to false), the user agent of the peer that did not initiate the
    234    creation process MUST queue a task to run the following steps:
    235      2. Let channel be a newly created RTCDataChannel object.
    236      3. Let configuration be an information bundle received from the other peer as a part of the
    237         process to establish the underlying data transport described by the WebRTC DataChannel
    238         Protocol specification [RTCWEB-DATA-PROTOCOL].
    239      4. Initialize channel's [[DataChannelLabel]], [[Ordered]], [[MaxPacketLifeTime]],
    240         [[MaxRetransmits]], [[DataChannelProtocol]], and [[DataChannelId]] internal slots to the
    241         corresponding values in configuration.
    242      5. Initialize channel's [[Negotiated]] internal slot to false.
    243      7. Set channel's [[ReadyState]] slot to connecting.
    244      8. Fire a datachannel event named datachannel with channel at the RTCPeerConnection object.
    245 
    246    Note: More exhaustive tests are defined in RTCDataChannel-dcep
    247 */
    248 
    249 promise_test(async (t) => {
    250  const resolver = new Resolver();
    251  const pc1 = new RTCPeerConnection();
    252  const pc2 = new RTCPeerConnection();
    253  t.add_cleanup(() => pc1.close());
    254  t.add_cleanup(() => pc2.close());
    255 
    256  const dc1 = pc1.createDataChannel('test', {
    257    ordered: false,
    258    maxRetransmits: 1,
    259    protocol: 'custom'
    260  });
    261 
    262  assert_equals(dc1.label, 'test');
    263  assert_equals(dc1.ordered, false);
    264  assert_equals(dc1.maxPacketLifeTime, null);
    265  assert_equals(dc1.maxRetransmits, 1);
    266  assert_equals(dc1.protocol, 'custom');
    267  assert_equals(dc1.negotiated, false);
    268 
    269  pc2.ondatachannel = t.step_func((event) => {
    270    const dc2 = event.channel;
    271    assert_true(dc2 instanceof RTCDataChannel,
    272      'Expect channel to be instance of RTCDataChannel');
    273 
    274    assert_equals(dc2.label, 'test');
    275    assert_equals(dc2.ordered, false);
    276    assert_equals(dc2.maxPacketLifeTime, null);
    277    assert_equals(dc2.maxRetransmits, 1);
    278    assert_equals(dc2.protocol, 'custom');
    279    assert_equals(dc2.negotiated, false);
    280    assert_equals(dc2.id, dc1.id);
    281 
    282    resolver.resolve();
    283  });
    284 
    285  exchangeIceCandidates(pc1, pc2);
    286  await exchangeOfferAnswer(pc1, pc2);
    287 
    288  await resolver;
    289 }, 'In-band negotiated channel created on remote peer should match the same configuration as local ' +
    290  'peer');
    291 
    292 promise_test(async (t) => {
    293  const resolver = new Resolver();
    294  const pc1 = new RTCPeerConnection();
    295  const pc2 = new RTCPeerConnection();
    296  t.add_cleanup(() => pc1.close());
    297  t.add_cleanup(() => pc2.close());
    298 
    299  const dc1 = pc1.createDataChannel('');
    300 
    301  assert_equals(dc1.label, '');
    302  assert_equals(dc1.ordered, true);
    303  assert_equals(dc1.maxPacketLifeTime, null);
    304  assert_equals(dc1.maxRetransmits, null);
    305  assert_equals(dc1.protocol, '');
    306  assert_equals(dc1.negotiated, false);
    307 
    308  pc2.ondatachannel = t.step_func((event) => {
    309    const dc2 = event.channel;
    310    assert_true(dc2 instanceof RTCDataChannel,
    311      'Expect channel to be instance of RTCDataChannel');
    312 
    313    assert_equals(dc2.label, '');
    314    assert_equals(dc2.ordered, true);
    315    assert_equals(dc2.maxPacketLifeTime, null);
    316    assert_equals(dc2.maxRetransmits, null);
    317    assert_equals(dc2.protocol, '');
    318    assert_equals(dc2.negotiated, false);
    319    assert_equals(dc2.id, dc1.id);
    320 
    321    resolver.resolve();
    322  });
    323 
    324  exchangeIceCandidates(pc1, pc2);
    325  await exchangeOfferAnswer(pc1, pc2);
    326 
    327  await resolver;
    328 }, 'In-band negotiated channel created on remote peer should match the same (default) ' +
    329  'configuration as local peer');
    330 
    331 /*
    332  6.2.  RTCDataChannel
    333    Dictionary RTCDataChannelInit Members
    334      negotiated
    335        The default value of false tells the user agent to announce the
    336        channel in-band and instruct the other peer to dispatch a corresponding
    337        RTCDataChannel object. If set to true, it is up to the application
    338        to negotiate the channel and create a RTCDataChannel object with the
    339        same id at the other peer.
    340 */
    341 promise_test(async (t) => {
    342  const resolver = new Resolver();
    343  const pc1 = new RTCPeerConnection();
    344  const pc2 = new RTCPeerConnection();
    345  t.add_cleanup(() => pc1.close());
    346  t.add_cleanup(() => pc2.close());
    347 
    348  pc2.ondatachannel = t.unreached_func('datachannel event should not be fired');
    349 
    350  pc1.createDataChannel('test', {
    351    negotiated: true,
    352    id: 42
    353  });
    354 
    355  exchangeIceCandidates(pc1, pc2);
    356  await exchangeOfferAnswer(pc1, pc2);
    357 
    358  // Wait a bit to ensure the 'datachannel' event does NOT fire
    359  t.step_timeout(() => resolver.resolve(), 500);
    360  await resolver;
    361 }, 'Negotiated channel should not fire datachannel event on remote peer');
    362 
    363 /*
    364  Non-testable
    365  6.2.  RTCDataChannel
    366    When an underlying data transport is to be announced
    367      1.  If the associated RTCPeerConnection object's [[isClosed]] slot
    368          is true, abort these steps.
    369 
    370  The above step is not testable because to reach it we would have to
    371  close the peer connection just between receiving the in-band negotiated data
    372  channel via DCEP and firing the datachannel event.
    373 */
    374 </script>