tor-browser

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

RollbackEvents.https.html (9454B)


      1 <!doctype html>
      2 <meta charset=utf-8>
      3 <script src="/resources/testharness.js"></script>
      4 <script src="/resources/testharnessreport.js"></script>
      5 <script>
      6 'use strict';
      7 
      8 ['audio', 'video'].forEach((kind) => {
      9  // Make sure "ontrack" fires if a prevuously rolled back track is added back.
     10  promise_test(async t => {
     11    const constraints = {};
     12    constraints[kind] = true;
     13    const stream = await navigator.mediaDevices.getUserMedia(constraints);
     14    const [track] = stream.getTracks();
     15    t.add_cleanup(() => track.stop());
     16 
     17    const pc1 = new RTCPeerConnection();
     18    t.add_cleanup(() => pc1.close());
     19    const pc2 = new RTCPeerConnection();
     20    t.add_cleanup(() => pc2.close());
     21 
     22    pc1.addTrack(track, stream);
     23    pc2.addTrack(track, stream);
     24    const [pc1Transceiver] = pc1.getTransceivers();
     25    const [pc2Transceiver] = pc2.getTransceivers();
     26 
     27    let remoteStreamViaOnTrackPromise = getRemoteStreamViaOnTrackPromise(pc2);
     28 
     29    // Apply remote offer, but don't complete the entire exchange.
     30    await pc1.setLocalDescription();
     31    await pc2.setRemoteDescription(pc1.localDescription);
     32    // The addTrack-transceiver gets associated, no need for a second
     33    // transceiver.
     34    assert_equals(pc2.getTransceivers().length, 1);
     35    const remoteStream = await remoteStreamViaOnTrackPromise;
     36    assert_equals(remoteStream.id, stream.id);
     37 
     38    const onRemoveTrackPromise = new Promise(r => {
     39      remoteStream.onremovetrack = () => { r(); };
     40    });
     41 
     42    // Cause track removal due to rollback.
     43    await pc2.setRemoteDescription({type:'rollback'});
     44    // The track was removed.
     45    await onRemoveTrackPromise;
     46 
     47    // Sanity check that ontrack still fires if we add it back again by applying
     48    // the same remote offer.
     49    remoteStreamViaOnTrackPromise = getRemoteStreamViaOnTrackPromise(pc2);
     50    await pc2.setRemoteDescription(pc1.localDescription);
     51    const revivedRemoteStream = await remoteStreamViaOnTrackPromise;
     52    // This test only expects IDs to be the same. The same stream object should
     53    // also be used, but this should be covered by separate tests.
     54    // TODO(https://crbug.com/1321738): Add MediaStream identity tests.
     55    assert_equals(remoteStream.id, revivedRemoteStream.id);
     56    // No cheating, the same transciever should be used as before.
     57    assert_equals(pc2.getTransceivers().length, 1);
     58  }, `[${kind}] Track with stream: removal due to disassociation in rollback and then add it back again`);
     59 
     60  // This is the same test as above, but this time without any remote streams.
     61  // This test could fail if [[FiredDirection]] was not reset in a rollback but
     62  // the above version of the test might still pass due to the track being
     63  // re-added to its stream.
     64  promise_test(async t => {
     65    const constraints = {};
     66    constraints[kind] = true;
     67    const stream = await navigator.mediaDevices.getUserMedia(constraints);
     68    const [track] = stream.getTracks();
     69    t.add_cleanup(() => track.stop());
     70 
     71    const pc1 = new RTCPeerConnection();
     72    t.add_cleanup(() => pc1.close());
     73    const pc2 = new RTCPeerConnection();
     74    t.add_cleanup(() => pc2.close());
     75 
     76    pc1.addTrack(track);
     77    pc2.addTrack(track);
     78    const [pc1Transceiver] = pc1.getTransceivers();
     79    const [pc2Transceiver] = pc2.getTransceivers();
     80 
     81    let remoteTrackPromise = getTrackViaOnTrackPromise(pc2);
     82 
     83    // Apply remote offer, but don't complete the entire exchange.
     84    await pc1.setLocalDescription();
     85    await pc2.setRemoteDescription(pc1.localDescription);
     86    // The addTrack-transceiver gets associated, no need for a second
     87    // transceiver.
     88    assert_equals(pc2.getTransceivers().length, 1);
     89    const remoteTrack = await remoteTrackPromise;
     90    assert_not_equals(remoteTrack, null);
     91 
     92    // Cause track removal due to rollback.
     93    await pc2.setRemoteDescription({type:'rollback'});
     94    // There's nothing equivalent to stream.onremovetrack when you don't have a
     95    // stream, but the track should become muted (if it isn't already).
     96    if (!remoteTrack.muted) {
     97      await new Promise(r => remoteTrack.onmute = () => { r(); });
     98    }
     99    assert_equals(remoteTrack.muted, true);
    100 
    101    // Sanity check that ontrack still fires if we add it back again by applying
    102    // the same remote offer.
    103    remoteTrackPromise = getTrackViaOnTrackPromise(pc2);
    104    await pc2.setRemoteDescription(pc1.localDescription);
    105    const revivedRemoteTrack = await remoteTrackPromise;
    106    // We can be sure the same track is used, because the same transceiver is
    107    // used (and transciever.receiver.track has same lifetime as transceiver).
    108    assert_equals(pc2.getTransceivers().length, 1);
    109    assert_equals(remoteTrack, revivedRemoteTrack);
    110  }, `[${kind}] Track without stream: removal due to disassociation in rollback and then add it back`);
    111 
    112  // Make sure "ontrack" can fire in a rollback (undo making it inactive).
    113  promise_test(async t => {
    114    const constraints = {};
    115    constraints[kind] = true;
    116    const stream = await navigator.mediaDevices.getUserMedia(constraints);
    117    const [track] = stream.getTracks();
    118    t.add_cleanup(() => track.stop());
    119 
    120    const pc1 = new RTCPeerConnection();
    121    t.add_cleanup(() => pc1.close());
    122    const pc2 = new RTCPeerConnection();
    123    t.add_cleanup(() => pc2.close());
    124 
    125    pc1.addTrack(track, stream);
    126    const [pc1Transceiver] = pc1.getTransceivers();
    127 
    128    let remoteStreamViaOnTrackPromise = getRemoteStreamViaOnTrackPromise(pc2);
    129 
    130    // Complete O/A exchange such that the transceiver gets associated.
    131    await pc1.setLocalDescription();
    132    await pc2.setRemoteDescription(pc1.localDescription);
    133    await pc2.setLocalDescription();
    134    await pc1.setRemoteDescription(pc2.localDescription);
    135    const [pc2Transceiver] = pc2.getTransceivers();
    136    assert_equals(pc2Transceiver.direction, 'recvonly');
    137    assert_equals(pc2Transceiver.currentDirection, 'recvonly');
    138 
    139    const remoteStream = await remoteStreamViaOnTrackPromise;
    140    assert_equals(remoteStream.id, stream.id);
    141    const onRemoveTrackPromise = new Promise(r => {
    142      remoteStream.onremovetrack = () => { r(); };
    143    });
    144 
    145    // Cause track removal.
    146    pc1Transceiver.direction = 'inactive';
    147    await pc1.setLocalDescription();
    148    await pc2.setRemoteDescription(pc1.localDescription);
    149    // The track was removed.
    150    await onRemoveTrackPromise;
    151 
    152    // Rolling back the offer revives the track, causing ontrack to fire again.
    153    remoteStreamViaOnTrackPromise = getRemoteStreamViaOnTrackPromise(pc2);
    154    await pc2.setRemoteDescription({type:'rollback'});
    155    const revivedRemoteStream = await remoteStreamViaOnTrackPromise;
    156    // This test only expects IDs to be the same. The same stream object should
    157    // also be used, but this should be covered by separate tests.
    158    // TODO(https://crbug.com/1321738): Add MediaStream identity tests.
    159    assert_equals(remoteStream.id, revivedRemoteStream.id);
    160  }, `[${kind}] Track with stream: removal due to direction changing and then add back using rollback`);
    161 
    162  // Same test as above but without remote streams.
    163  promise_test(async t => {
    164    const constraints = {};
    165    constraints[kind] = true;
    166    const stream = await navigator.mediaDevices.getUserMedia(constraints);
    167    const [track] = stream.getTracks();
    168    t.add_cleanup(() => track.stop());
    169 
    170    const pc1 = new RTCPeerConnection();
    171    t.add_cleanup(() => pc1.close());
    172    const pc2 = new RTCPeerConnection();
    173    t.add_cleanup(() => pc2.close());
    174 
    175    pc1.addTrack(track);
    176    const [pc1Transceiver] = pc1.getTransceivers();
    177 
    178    let remoteTrackPromise = getTrackViaOnTrackPromise(pc2);
    179 
    180    // Complete O/A exchange such that the transceiver gets associated.
    181    await pc1.setLocalDescription();
    182    await pc2.setRemoteDescription(pc1.localDescription);
    183    await pc2.setLocalDescription();
    184    await pc1.setRemoteDescription(pc2.localDescription);
    185    const [pc2Transceiver] = pc2.getTransceivers();
    186    assert_equals(pc2Transceiver.direction, 'recvonly');
    187    assert_equals(pc2Transceiver.currentDirection, 'recvonly');
    188 
    189    const remoteTrack = await remoteTrackPromise;
    190 
    191    // Cause track removal.
    192    pc1Transceiver.direction = 'inactive';
    193    await pc1.setLocalDescription();
    194    await pc2.setRemoteDescription(pc1.localDescription);
    195    // There's nothing equivalent to stream.onremovetrack when you don't have a
    196    // stream, but the track should become muted (if it isn't already).
    197    if (!remoteTrack.muted) {
    198      await new Promise(r => remoteTrack.onmute = () => { r(); });
    199    }
    200    assert_equals(remoteTrack.muted, true);
    201 
    202    // Rolling back the offer revives the track, causing ontrack to fire again.
    203    remoteTrackPromise = getTrackViaOnTrackPromise(pc2);
    204    await pc2.setRemoteDescription({type:'rollback'});
    205    const revivedRemoteTrack = await remoteTrackPromise;
    206    // We can be sure the same track is used, because the same transceiver is
    207    // used (and transciever.receiver.track has same lifetime as transceiver).
    208    assert_equals(pc2.getTransceivers().length, 1);
    209    assert_equals(remoteTrack, revivedRemoteTrack);
    210  }, `[${kind}] Track without stream: removal due to direction changing and then add back using rollback`);
    211 });
    212 
    213 function getTrackViaOnTrackPromise(pc) {
    214  return new Promise(r => {
    215    pc.ontrack = e => {
    216      pc.ontrack = null;
    217      r(e.track);
    218    };
    219  });
    220 }
    221 
    222 function getRemoteStreamViaOnTrackPromise(pc) {
    223  return new Promise(r => {
    224    pc.ontrack = e => {
    225      pc.ontrack = null;
    226      r(e.streams[0]);
    227    };
    228  });
    229 }
    230 
    231 </script>