tor-browser

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

RTCPeerConnection-removeTrack.https.html (13618B)


      1 <!doctype html>
      2 <meta charset=utf-8>
      3 <title>RTCPeerConnection.prototype.removeTrack</title>
      4 <script src="/resources/testharness.js"></script>
      5 <script src="/resources/testharnessreport.js"></script>
      6 <script src="RTCPeerConnection-helper.js"></script>
      7 <script>
      8  'use strict';
      9 
     10  // Test is based on the following editor draft:
     11  // https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.html
     12 
     13  // The following helper functions are called from RTCPeerConnection-helper.js:
     14  // generateAnswer
     15 
     16  /*
     17    5.1.  RTCPeerConnection Interface Extensions
     18      partial interface RTCPeerConnection {
     19        ...
     20        void                removeTrack(RTCRtpSender sender);
     21        RTCRtpTransceiver   addTransceiver((MediaStreamTrack or DOMString) trackOrKind,
     22                                                   optional RTCRtpTransceiverInit init);
     23      };
     24   */
     25 
     26  // Before calling removeTrack can be tested, one needs to add MediaStreamTracks to
     27  // a peer connection. There are two ways for adding MediaStreamTrack: addTrack and
     28  // addTransceiver. addTransceiver is a newer API while addTrack has been implemented
     29  // in current browsers for some time. As a result some of the removeTrack tests have
     30  // two versions so that removeTrack can be partially tested without addTransceiver
     31  // and the transceiver APIs being implemented.
     32 
     33  /*
     34    5.1.  removeTrack
     35      3.  If connection's [[isClosed]] slot is true, throw an InvalidStateError.
     36   */
     37  promise_test(async t => {
     38    const pc = new RTCPeerConnection();
     39    const stream = await getNoiseStream({audio: true});
     40    t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
     41    const [track] = stream.getTracks();
     42    const transceiver = pc.addTransceiver(track);
     43    const { sender } = transceiver;
     44 
     45    pc.close();
     46    assert_throws_dom('InvalidStateError', () => pc.removeTrack(sender));
     47  }, 'addTransceiver - Calling removeTrack when connection is closed should throw InvalidStateError');
     48 
     49  promise_test(async t => {
     50    const pc = new RTCPeerConnection();
     51    t.add_cleanup(() => pc.close());
     52 
     53    const stream = await getNoiseStream({ audio: true });
     54    t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
     55    const [track] = stream.getTracks();
     56    const sender = pc.addTrack(track, stream);
     57 
     58    pc.close();
     59    assert_throws_dom('InvalidStateError', () => pc.removeTrack(sender));
     60  }, 'addTrack - Calling removeTrack when connection is closed should throw InvalidStateError');
     61 
     62  promise_test(async t => {
     63    const pc = new RTCPeerConnection();
     64    t.add_cleanup(() => pc.close());
     65    const stream = await getNoiseStream({audio: true});
     66    t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
     67    const [track] = stream.getTracks();
     68    const transceiver = pc.addTransceiver(track);
     69    const { sender } = transceiver;
     70 
     71    const pc2 = new RTCPeerConnection();
     72    pc2.close();
     73    assert_throws_dom('InvalidStateError', () => pc2.removeTrack(sender));
     74  }, 'addTransceiver - Calling removeTrack on different connection that is closed should throw InvalidStateError');
     75 
     76  promise_test(async t => {
     77    const pc = new RTCPeerConnection();
     78    t.add_cleanup(() => pc.close());
     79 
     80    const stream = await getNoiseStream({ audio: true });
     81    t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
     82    const [track] = stream.getTracks();
     83    const sender = pc.addTrack(track, stream);
     84 
     85    const pc2 = new RTCPeerConnection();
     86    pc2.close();
     87    assert_throws_dom('InvalidStateError', () => pc2.removeTrack(sender));
     88  }, 'addTrack - Calling removeTrack on different connection that is closed should throw InvalidStateError');
     89 
     90  /*
     91    5.1.  removeTrack
     92      4.  If sender was not created by connection, throw an InvalidAccessError.
     93   */
     94  promise_test(async t => {
     95    const pc = new RTCPeerConnection();
     96    t.add_cleanup(() => pc.close());
     97    const stream = await getNoiseStream({audio: true});
     98    t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
     99    const [track] = stream.getTracks();
    100    const transceiver = pc.addTransceiver(track);
    101    const { sender } = transceiver;
    102 
    103    const pc2 = new RTCPeerConnection();
    104    t.add_cleanup(() => pc2.close());
    105    assert_throws_dom('InvalidAccessError', () => pc2.removeTrack(sender));
    106  }, 'addTransceiver - Calling removeTrack on different connection should throw InvalidAccessError');
    107 
    108  promise_test(async t => {
    109    const pc = new RTCPeerConnection();
    110    t.add_cleanup(() => pc.close());
    111 
    112    const stream = await getNoiseStream({ audio: true });
    113    t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
    114    const [track] = stream.getTracks();
    115    const sender = pc.addTrack(track, stream);
    116 
    117    const pc2 = new RTCPeerConnection();
    118    t.add_cleanup(() => pc2.close());
    119    assert_throws_dom('InvalidAccessError', () => pc2.removeTrack(sender));
    120  }, 'addTrack - Calling removeTrack on different connection should throw InvalidAccessError')
    121 
    122  /*
    123    5.1.  removeTrack
    124      7.  Set sender.track to null.
    125   */
    126  promise_test(async t => {
    127    const pc = new RTCPeerConnection();
    128    t.add_cleanup(() => pc.close());
    129    const stream = await getNoiseStream({audio: true});
    130    t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
    131    const [track] = stream.getTracks();
    132    const transceiver = pc.addTransceiver(track);
    133    const { sender } = transceiver;
    134 
    135    assert_equals(sender.track, track);
    136    assert_equals(transceiver.direction, 'sendrecv');
    137    assert_equals(transceiver.currentDirection, null);
    138 
    139    pc.removeTrack(sender);
    140    assert_equals(sender.track, null);
    141    assert_equals(transceiver.direction, 'recvonly');
    142  }, 'addTransceiver - Calling removeTrack with valid sender should set sender.track to null');
    143 
    144  promise_test(async t => {
    145    const pc = new RTCPeerConnection();
    146    t.add_cleanup(() => pc.close());
    147 
    148    const stream = await getNoiseStream({ audio: true });
    149    t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
    150    const [track] = stream.getTracks();
    151    const sender = pc.addTrack(track, stream);
    152 
    153    assert_equals(sender.track, track);
    154 
    155    pc.removeTrack(sender);
    156    assert_equals(sender.track, null);
    157  }, 'addTrack - Calling removeTrack with valid sender should set sender.track to null');
    158 
    159  /*
    160    5.1.  removeTrack
    161      7.  Set sender.track to null.
    162      10. If transceiver.currentDirection is sendrecv set transceiver.direction
    163          to recvonly.
    164   */
    165  promise_test(async t => {
    166    const caller = new RTCPeerConnection();
    167    t.add_cleanup(() => caller.close());
    168    const callee = new RTCPeerConnection();
    169    t.add_cleanup(() => callee.close());
    170    const stream = await getNoiseStream({audio: true});
    171    t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
    172    const [track] = stream.getTracks();
    173    const transceiver = caller.addTransceiver(track);
    174    const { sender } = transceiver;
    175 
    176    assert_equals(sender.track, track);
    177    assert_equals(transceiver.direction, 'sendrecv');
    178    assert_equals(transceiver.currentDirection, null);
    179 
    180    const offer = await caller.createOffer();
    181    await caller.setLocalDescription(offer);
    182    await callee.setRemoteDescription(offer);
    183    callee.addTrack(track, stream);
    184    const answer = await callee.createAnswer();
    185    await callee.setLocalDescription(answer);
    186    await caller.setRemoteDescription(answer);
    187    assert_equals(transceiver.currentDirection, 'sendrecv');
    188 
    189    caller.removeTrack(sender);
    190    assert_equals(sender.track, null);
    191    assert_equals(transceiver.direction, 'recvonly');
    192    assert_equals(transceiver.currentDirection, 'sendrecv',
    193      'Expect currentDirection to not change');
    194  }, 'Calling removeTrack with currentDirection sendrecv should set direction to recvonly');
    195 
    196  /*
    197    5.1.  removeTrack
    198      7.  Set sender.track to null.
    199      11. If transceiver.currentDirection is sendonly set transceiver.direction
    200          to inactive.
    201   */
    202  promise_test(async t => {
    203    const pc = new RTCPeerConnection();
    204    t.add_cleanup(() => pc.close());
    205    const stream = await getNoiseStream({audio: true});
    206    t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
    207    const [track] = stream.getTracks();
    208    const transceiver = pc.addTransceiver(track, { direction: 'sendonly' });
    209    const { sender } = transceiver;
    210 
    211    assert_equals(sender.track, track);
    212    assert_equals(transceiver.direction, 'sendonly');
    213    assert_equals(transceiver.currentDirection, null);
    214 
    215    const offer = await pc.createOffer();
    216    await pc.setLocalDescription(offer);
    217    const answer = await generateAnswer(offer);
    218    await pc.setRemoteDescription(answer);
    219    assert_equals(transceiver.currentDirection, 'sendonly');
    220 
    221    pc.removeTrack(sender);
    222    assert_equals(sender.track, null);
    223    assert_equals(transceiver.direction, 'inactive');
    224    assert_equals(transceiver.currentDirection, 'sendonly',
    225      'Expect currentDirection to not change');
    226  }, 'Calling removeTrack with currentDirection sendonly should set direction to inactive');
    227 
    228  /*
    229    5.1.  removeTrack
    230      7.  Set sender.track to null.
    231      9.  If transceiver.currentDirection is recvonly or inactive,
    232          then abort these steps.
    233   */
    234  promise_test(async t => {
    235    const caller = new RTCPeerConnection();
    236    t.add_cleanup(() => caller.close());
    237    const callee = new RTCPeerConnection();
    238    t.add_cleanup(() => callee.close());
    239    const stream = await getNoiseStream({audio: true});
    240    t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
    241    const [track] = stream.getTracks();
    242    const transceiver = caller.addTransceiver(track, { direction: 'recvonly' });
    243    const { sender } = transceiver;
    244 
    245    assert_equals(sender.track, track);
    246    assert_equals(transceiver.direction, 'recvonly');
    247    assert_equals(transceiver.currentDirection, null);
    248 
    249    const offer = await caller.createOffer();
    250    await caller.setLocalDescription(offer);
    251    await callee.setRemoteDescription(offer);
    252    callee.addTrack(track, stream);
    253    const answer = await callee.createAnswer();
    254    await callee.setLocalDescription(answer);
    255    await caller.setRemoteDescription(answer);
    256    assert_equals(transceiver.currentDirection, 'recvonly');
    257 
    258    caller.removeTrack(sender);
    259    assert_equals(sender.track, null);
    260    assert_equals(transceiver.direction, 'recvonly');
    261    assert_equals(transceiver.currentDirection, 'recvonly');
    262  }, 'Calling removeTrack with currentDirection recvonly should not change direction');
    263 
    264  /*
    265    5.1.  removeTrack
    266      7.  Set sender.track to null.
    267      9.  If transceiver.currentDirection is recvonly or inactive,
    268          then abort these steps.
    269   */
    270  promise_test(async t => {
    271    const pc = new RTCPeerConnection();
    272    t.add_cleanup(() => pc.close());
    273    const stream = await getNoiseStream({audio: true});
    274    t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
    275    const [track] = stream.getTracks();
    276    const transceiver = pc.addTransceiver(track, { direction: 'inactive' });
    277    const { sender } = transceiver;
    278 
    279    assert_equals(sender.track, track);
    280    assert_equals(transceiver.direction, 'inactive');
    281    assert_equals(transceiver.currentDirection, null);
    282 
    283    const offer = await pc.createOffer();
    284    await pc.setLocalDescription(offer);
    285    const answer = await generateAnswer(offer);
    286    await pc.setRemoteDescription(answer);
    287    assert_equals(transceiver.currentDirection, 'inactive');
    288 
    289    pc.removeTrack(sender);
    290    assert_equals(sender.track, null);
    291    assert_equals(transceiver.direction, 'inactive');
    292    assert_equals(transceiver.currentDirection, 'inactive');
    293  }, 'Calling removeTrack with currentDirection inactive should not change direction');
    294 
    295  promise_test(async t => {
    296    const pc = new RTCPeerConnection();
    297    t.add_cleanup(() => pc.close());
    298    const stream = await getNoiseStream({audio: true});
    299    t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
    300    const [track] = stream.getTracks();
    301    const sender = pc.addTrack(track, stream);
    302 
    303    pc.getTransceivers()[0].stop();
    304    // TODO: Spec says this only sets [[Stopping]], not [[Stopped]]. Spec
    305    // might change: https://github.com/w3c/webrtc-pc/issues/2874
    306    pc.removeTrack(sender);
    307    assert_equals(sender.track, track);
    308  }, "Calling removeTrack on a stopped transceiver should be a no-op");
    309 
    310  promise_test(async t => {
    311    const pc = new RTCPeerConnection();
    312    t.add_cleanup(() => pc.close());
    313    const stream = await getNoiseStream({audio: true});
    314    t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
    315    const [track] = stream.getTracks();
    316    const sender = pc.addTrack(track, stream);
    317 
    318    await sender.replaceTrack(null);
    319    pc.removeTrack(sender);
    320    assert_equals(sender.track, null);
    321 }, "Calling removeTrack on a null track should have no effect");
    322 
    323 
    324  /*
    325    TODO
    326      5.1.  removeTrack
    327        Stops sending media from sender. The RTCRtpSender will still appear
    328        in getSenders. Doing so will cause future calls to createOffer to
    329        mark the media description for the corresponding transceiver as
    330        recvonly or inactive, as defined in [JSEP] (section 5.2.2.).
    331 
    332        When the other peer stops sending a track in this manner, an ended
    333        event is fired at the MediaStreamTrack object.
    334 
    335        6.  If sender is not in senders (which indicates that it was removed
    336            due to setting an RTCSessionDescription of type "rollback"),
    337            then abort these steps.
    338        12. Update the negotiation-needed flag for connection.
    339   */
    340 </script>