RTCPeerConnection-setLocalDescription-rollback.html (6840B)
1 <!doctype html> 2 <meta charset=utf-8> 3 <title>RTCPeerConnection.prototype.setLocalDescription rollback</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 // generateAudioReceiveOnlyOffer 15 16 /* 17 4.3.2. Interface Definition 18 [Constructor(optional RTCConfiguration configuration)] 19 interface RTCPeerConnection : EventTarget { 20 Promise<void> setLocalDescription( 21 RTCSessionDescriptionInit description); 22 23 readonly attribute RTCSessionDescription? localDescription; 24 readonly attribute RTCSessionDescription? currentLocalDescription; 25 readonly attribute RTCSessionDescription? pendingLocalDescription; 26 27 Promise<void> setRemoteDescription( 28 RTCSessionDescriptionInit description); 29 30 readonly attribute RTCSessionDescription? remoteDescription; 31 readonly attribute RTCSessionDescription? currentRemoteDescription; 32 readonly attribute RTCSessionDescription? pendingRemoteDescription; 33 ... 34 }; 35 36 4.6.2. RTCSessionDescription Class 37 dictionary RTCSessionDescriptionInit { 38 required RTCSdpType type; 39 DOMString sdp = ""; 40 }; 41 42 4.6.1. RTCSdpType 43 enum RTCSdpType { 44 "offer", 45 "pranswer", 46 "answer", 47 "rollback" 48 }; 49 */ 50 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 54 following steps: 55 - If description is of type "rollback", then this is a rollback. Set 56 connection.pendingLocalDescription to null and signaling state to stable. 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 pc.createOffer() 66 .then(offer => pc.setLocalDescription(offer)) 67 .then(() => { 68 assert_equals(pc.signalingState, 'have-local-offer'); 69 assert_not_equals(pc.localDescription, null); 70 assert_equals(pc.localDescription, pc.localDescription); 71 assert_equals(pc.pendingLocalDescription, pc.localDescription); 72 assert_equals(pc.currentLocalDescription, null); 73 74 return pc.setLocalDescription({ type: 'rollback' }); 75 }) 76 .then(() => { 77 assert_equals(pc.signalingState, 'stable'); 78 assert_equals(pc.localDescription, null); 79 assert_equals(pc.pendingLocalDescription, null); 80 assert_equals(pc.currentLocalDescription, null); 81 82 assert_array_equals(states, ['have-local-offer', 'stable']); 83 }); 84 }, 'setLocalDescription(rollback) from have-local-offer state should reset back to stable state'); 85 86 /* 87 4.3.1.6. Set the RTCSessionSessionDescription 88 2.3. If the description's type is invalid for the current signaling state of 89 connection, then reject p with a newly created InvalidStateError and abort 90 these steps. Note that this implies that once the answerer has performed 91 setLocalDescription with his answer, this cannot be rolled back. 92 93 [jsep] 94 4.1.8.2. Rollback 95 - Rollback can only be used to cancel proposed changes; 96 there is no support for rolling back from a stable state to a 97 previous stable state 98 */ 99 promise_test(t => { 100 const pc = new RTCPeerConnection(); 101 t.add_cleanup(() => pc.close()); 102 return promise_rejects_dom(t, 'InvalidStateError', 103 pc.setLocalDescription({ type: 'rollback' })); 104 }, `setLocalDescription(rollback) from stable state should reject with InvalidStateError`); 105 106 promise_test(t => { 107 const pc = new RTCPeerConnection(); 108 t.add_cleanup(() => pc.close()); 109 return generateAudioReceiveOnlyOffer(pc) 110 .then(offer => 111 pc.setRemoteDescription(offer) 112 .then(() => pc.createAnswer())) 113 .then(answer => pc.setLocalDescription(answer)) 114 .then(() => { 115 return promise_rejects_dom(t, 'InvalidStateError', 116 pc.setLocalDescription({ type: 'rollback' })); 117 }); 118 }, `setLocalDescription(rollback) after setting answer description should reject with InvalidStateError`); 119 120 promise_test(async t => { 121 const pc = new RTCPeerConnection(); 122 t.add_cleanup(() => pc.close()); 123 const offer = await generateAudioReceiveOnlyOffer(pc); 124 await pc.setRemoteDescription(offer); 125 await promise_rejects_dom(t, 'InvalidStateError', pc.setLocalDescription({ type: 'rollback' })); 126 }, `setLocalDescription(rollback) after setting a remote offer should reject with InvalidStateError`); 127 128 promise_test(t => { 129 const pc = new RTCPeerConnection(); 130 t.add_cleanup(() => pc.close()); 131 return pc.createOffer() 132 .then(offer => pc.setLocalDescription(offer)) 133 .then(() => pc.setLocalDescription({ 134 type: 'rollback', 135 sdp: '!<Invalid SDP Content>;' 136 })); 137 }, `setLocalDescription(rollback) should ignore invalid sdp content and succeed`); 138 139 promise_test(async t => { 140 const pc = new RTCPeerConnection(); 141 t.add_cleanup(() => pc.close()); 142 143 pc.addTransceiver('audio', { direction: 'recvonly' }); 144 await pc.setLocalDescription(await pc.createOffer()); 145 const sldPromise = pc.setLocalDescription({type: "rollback"}); 146 147 assert_equals(pc.signalingState, "have-local-offer", "signalingState should not be set synchronously after a call to sLD"); 148 149 assert_not_equals(pc.pendingLocalDescription, null, "pendingLocalDescription should not be set synchronously after a call to sLD"); 150 assert_equals(pc.pendingLocalDescription.type, "offer"); 151 assert_equals(pc.pendingLocalDescription, pc.localDescription); 152 assert_equals(pc.pendingRemoteDescription, null, "pendingRemoteDescription should never be set due to sLD(offer)"); 153 154 const stablePromise = new Promise(resolve => { 155 pc.onsignalingstatechange = () => { 156 resolve(pc.signalingState); 157 } 158 }); 159 const raceValue = await Promise.race([stablePromise, sldPromise]); 160 assert_equals(raceValue, "stable", "signalingstatechange event should fire before sLD resolves"); 161 assert_equals(pc.pendingLocalDescription, null, "pendingLocalDescription should be updated before the signalingstatechange event"); 162 assert_equals(pc.pendingRemoteDescription, null, "pendingRemoteDescription should never be set due to sLD(offer)"); 163 164 await sldPromise; 165 }, "setLocalDescription(rollback) should update internal state with a queued tassk, in the right order"); 166 167 </script>