tor-browser

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

RTCPeerConnection-setLocalDescription-answer.html (9546B)


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