tor-browser

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

RTCPeerConnection-setLocalDescription-offer.html (9603B)


      1 <!doctype html>
      2 <meta charset=utf-8>
      3 <title>RTCPeerConnection.prototype.setLocalDescription</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  //   generateDataChannelOffer
     15  //   assert_session_desc_not_similar
     16  //   assert_session_desc_similar
     17 
     18  /*
     19    4.3.2.  Interface Definition
     20      [Constructor(optional RTCConfiguration configuration)]
     21      interface RTCPeerConnection : EventTarget {
     22        Promise<void>                      setRemoteDescription(
     23            RTCSessionDescriptionInit description);
     24 
     25        readonly attribute RTCSessionDescription? remoteDescription;
     26        readonly attribute RTCSessionDescription? currentRemoteDescription;
     27        readonly attribute RTCSessionDescription? pendingRemoteDescription;
     28        ...
     29      };
     30 
     31    4.6.2.  RTCSessionDescription Class
     32      dictionary RTCSessionDescriptionInit {
     33        required RTCSdpType type;
     34                 DOMString  sdp = "";
     35      };
     36 
     37    4.6.1.  RTCSdpType
     38      enum RTCSdpType {
     39        "offer",
     40        "pranswer",
     41        "answer",
     42        "rollback"
     43      };
     44   */
     45 
     46  /*
     47    4.3.2.  setLocalDescription
     48      2.  Let lastOffer be the result returned by the last call to createOffer.
     49      5.  If description.sdp is null and description.type is offer, set description.sdp
     50          to lastOffer.
     51 
     52    4.3.1.6.  Set the RTCSessionSessionDescription
     53      2.2.2.  If description is set as a local description, then run one of the following
     54              steps:
     55        - If description is of type "offer", set connection.pendingLocalDescription
     56          to description and signaling state to have-local-offer.
     57   */
     58  promise_test(t => {
     59    const pc = new RTCPeerConnection();
     60    t.add_cleanup(() => pc.close());
     61 
     62    const states = [];
     63    pc.addEventListener('signalingstatechange', () => states.push(pc.signalingState));
     64 
     65    return generateAudioReceiveOnlyOffer(pc)
     66    .then(offer =>
     67      pc.setLocalDescription(offer)
     68      .then(() => {
     69        assert_equals(pc.signalingState, 'have-local-offer');
     70        assert_session_desc_similar(pc.localDescription, offer);
     71        assert_equals(pc.pendingLocalDescription, pc.localDescription);
     72        assert_equals(pc.currentLocalDescription, null);
     73 
     74        assert_array_equals(states, ['have-local-offer']);
     75      }));
     76  }, 'setLocalDescription with valid offer should succeed');
     77 
     78  /*
     79    4.3.2.  setLocalDescription
     80      2.  Let lastOffer be the result returned by the last call to createOffer.
     81      5.  If description.sdp is null and description.type is offer, set description.sdp
     82          to lastOffer.
     83   */
     84  promise_test(t => {
     85    const pc = new RTCPeerConnection();
     86    t.add_cleanup(() => pc.close());
     87    return generateAudioReceiveOnlyOffer(pc)
     88    .then(offer =>
     89      pc.setLocalDescription({ type: 'offer' })
     90      .then(() => {
     91        assert_equals(pc.signalingState, 'have-local-offer');
     92        assert_session_desc_similar(pc.localDescription, offer);
     93        assert_equals(pc.pendingLocalDescription, pc.localDescription);
     94        assert_equals(pc.currentLocalDescription, null);
     95      }));
     96  }, 'setLocalDescription with type offer and null sdp should use lastOffer generated from createOffer');
     97 
     98  /*
     99    4.3.2.  setLocalDescription
    100      2.  Let lastOffer be the result returned by the last call to createOffer.
    101      6.  If description.type is offer and description.sdp does not match lastOffer,
    102          reject the promise with a newly created InvalidModificationError and abort
    103          these steps.
    104   */
    105  promise_test(t => {
    106    const pc = new RTCPeerConnection();
    107    t.add_cleanup(() => pc.close());
    108    const pc2 = new RTCPeerConnection();
    109 
    110    t.add_cleanup(() => pc2.close());
    111 
    112    return generateDataChannelOffer(pc)
    113    .then(offer => pc2.setLocalDescription(offer))
    114    .then(() => t.unreached_func("setLocalDescription should have rejected"),
    115          (error) => assert_equals(error.name, 'InvalidModificationError'));
    116  }, 'setLocalDescription() with offer not created by own createOffer() should reject with InvalidModificationError');
    117 
    118  promise_test(t => {
    119    // Create first offer with audio line, then second offer with
    120    // both audio and video line. Since the second offer is the
    121    // last offer, setLocalDescription would reject when setting
    122    // with the first offer
    123    const pc = new RTCPeerConnection();
    124    t.add_cleanup(() => pc.close());
    125    return generateAudioReceiveOnlyOffer(pc)
    126    .then(offer1 =>
    127      generateVideoReceiveOnlyOffer(pc)
    128      .then(offer2 => {
    129        assert_session_desc_not_similar(offer1, offer2);
    130        return promise_rejects_dom(t, 'InvalidModificationError',
    131          pc.setLocalDescription(offer1));
    132      }));
    133  }, 'Set created offer other than last offer should reject with InvalidModificationError');
    134 
    135  promise_test(t => {
    136    const pc = new RTCPeerConnection();
    137    t.add_cleanup(() => pc.close());
    138 
    139    const states = [];
    140    pc.addEventListener('signalingstatechange', () => states.push(pc.signalingState));
    141 
    142    return generateAudioReceiveOnlyOffer(pc)
    143    .then(offer1 =>
    144      pc.setLocalDescription(offer1)
    145      .then(() =>
    146        generateVideoReceiveOnlyOffer(pc)
    147        .then(offer2 =>
    148          pc.setLocalDescription(offer2)
    149          .then(() => {
    150            assert_session_desc_not_similar(offer1, offer2);
    151            assert_equals(pc.signalingState, 'have-local-offer');
    152            assert_session_desc_similar(pc.localDescription, offer2);
    153            assert_session_desc_similar(pc.pendingLocalDescription, offer2);
    154            assert_equals(pc.currentLocalDescription, null);
    155 
    156            assert_array_equals(states, ['have-local-offer']);
    157          }))));
    158  }, 'Creating and setting offer multiple times should succeed');
    159 
    160  promise_test(async t => {
    161    const pc1 = new RTCPeerConnection();
    162    t.add_cleanup(() => pc1.close());
    163    const pc2 = new RTCPeerConnection();
    164    t.add_cleanup(() => pc2.close());
    165 
    166    pc1.addTransceiver('audio', { direction: 'recvonly' });
    167    const offer = await pc1.createOffer(); // [[LastOffer]] set
    168    pc2.addTransceiver('video', { direction: 'recvonly' });
    169    const offer2 = await pc2.createOffer();
    170    await pc1.setRemoteDescription(offer2);
    171    await pc1.createAnswer(); // [[LastAnswer]] set
    172    await pc1.setRemoteDescription({type: "rollback"});
    173    await pc1.setLocalDescription(offer);
    174  }, "Setting previously generated offer after a call to createAnswer should work");
    175 
    176  promise_test(async t => {
    177    const pc1 = new RTCPeerConnection();
    178    t.add_cleanup(() => pc1.close());
    179    const pc2 = new RTCPeerConnection();
    180    t.add_cleanup(() => pc2.close());
    181 
    182    pc1.addTransceiver('audio', { direction: 'recvonly' });
    183    await pc1.setLocalDescription(await pc1.createOffer());
    184 
    185    const offer = await pc1.createOffer();
    186    await pc1.setLocalDescription(offer);
    187    await pc2.setRemoteDescription(offer);
    188    const answer = await pc2.createAnswer();
    189    await pc2.setLocalDescription(answer);
    190    await pc1.setRemoteDescription(answer);
    191 
    192    assert_equals(pc1.getTransceivers().length, 1);
    193    assert_equals(pc1.getTransceivers()[0].receiver.track.kind, "audio");
    194    assert_equals(pc2.getTransceivers().length, 1);
    195    assert_equals(pc2.getTransceivers()[0].receiver.track.kind, "audio");
    196  }, "Negotiation works when there has been a repeated setLocalDescription(offer)");
    197 
    198  promise_test(async t => {
    199    const pc = new RTCPeerConnection();
    200    t.add_cleanup(() => pc.close());
    201 
    202    pc.addTransceiver('audio', { direction: 'recvonly' });
    203    const sldPromise = pc.setLocalDescription(await pc.createOffer());
    204 
    205    assert_equals(pc.signalingState, "stable", "signalingState should not be set synchronously after a call to sLD");
    206 
    207    assert_equals(pc.pendingLocalDescription, null, "pendingRemoteDescription should never be set due to sLD");
    208    assert_equals(pc.pendingRemoteDescription, null, "pendingLocalDescription should not be set synchronously after a call to sLD");
    209    assert_equals(pc.currentLocalDescription, null, "currentLocalDescription should not be set synchronously after a call to sLD");
    210    assert_equals(pc.currentRemoteDescription, null, "currentRemoteDescription should not be set synchronously after a call to sLD");
    211 
    212    const statePromise = new Promise(resolve => {
    213      pc.onsignalingstatechange = () => {
    214        resolve(pc.signalingState);
    215      }
    216    });
    217    const raceValue = await Promise.race([statePromise, sldPromise]);
    218    assert_equals(raceValue, "have-local-offer", "signalingstatechange event should fire before sLD resolves");
    219    assert_equals(pc.pendingRemoteDescription, null, "pendingRemoteDescription should never be set due to sLD");
    220    assert_not_equals(pc.pendingLocalDescription, null, "pendingLocalDescription should be updated before the signalingstatechange event");
    221    assert_equals(pc.pendingLocalDescription.type, "offer");
    222    assert_equals(pc.pendingLocalDescription, pc.localDescription);
    223    assert_equals(pc.currentLocalDescription, null, "currentLocalDescription should never be updated due to sLD(offer)");
    224    assert_equals(pc.currentRemoteDescription, null, "currentRemoteDescription should never be updated due to sLD(offer)");
    225 
    226    await sldPromise;
    227  }, "setLocalDescription(offer) should update internal state with a queued task, in the right order");
    228 
    229 </script>