1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
|
<!doctype html>
<meta charset=utf-8>
<title>RTCPeerConnection.prototype.setRemoteDescription rollback</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="RTCPeerConnection-helper.js"></script>
<script>
'use strict';
// Test is based on the following editor draft:
// https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.html
// The following helper functions are called from RTCPeerConnection-helper.js:
// assert_session_desc_similar
// generateAudioReceiveOnlyOffer
// generateDataChannelOffer
/*
4.3.2. Interface Definition
[Constructor(optional RTCConfiguration configuration)]
interface RTCPeerConnection : EventTarget {
Promise<void> setLocalDescription(
RTCSessionDescriptionInit description);
readonly attribute RTCSessionDescription? localDescription;
readonly attribute RTCSessionDescription? currentLocalDescription;
readonly attribute RTCSessionDescription? pendingLocalDescription;
Promise<void> setRemoteDescription(
RTCSessionDescriptionInit description);
readonly attribute RTCSessionDescription? remoteDescription;
readonly attribute RTCSessionDescription? currentRemoteDescription;
readonly attribute RTCSessionDescription? pendingRemoteDescription;
...
};
4.6.2. RTCSessionDescription Class
dictionary RTCSessionDescriptionInit {
required RTCSdpType type;
DOMString sdp = "";
};
4.6.1. RTCSdpType
enum RTCSdpType {
"offer",
"pranswer",
"answer",
"rollback"
};
*/
/*
4.3.1.6. Set the RTCSessionSessionDescription
2.2.3. Otherwise, if description is set as a remote description, then run one
of the following steps:
- If description is of type "rollback", then this is a rollback.
Set connection.pendingRemoteDescription to null and signaling state to stable.
*/
promise_test(t => {
const pc = new RTCPeerConnection();
t.add_cleanup(() => pc.close());
const states = [];
pc.addEventListener('signalingstatechange', () => states.push(pc.signalingState));
return generateDataChannelOffer(pc)
.then(offer => pc.setRemoteDescription(offer))
.then(() => {
assert_equals(pc.signalingState, 'have-remote-offer');
assert_not_equals(pc.remoteDescription, null);
assert_not_equals(pc.pendingRemoteDescription, null);
assert_equals(pc.currentRemoteDescription, null);
return pc.setRemoteDescription({ type: 'rollback' });
})
.then(() => {
assert_equals(pc.signalingState, 'stable');
assert_equals(pc.remoteDescription, null);
assert_equals(pc.pendingRemoteDescription, null);
assert_equals(pc.currentRemoteDescription, null);
assert_array_equals(states, ['have-remote-offer', 'stable']);
});
}, 'setRemoteDescription(rollback) in have-remote-offer state should revert to stable state');
/*
4.3.1.6. Set the RTCSessionSessionDescription
2.3. If the description's type is invalid for the current signaling state of
connection, then reject p with a newly created InvalidStateError and abort
these steps.
[jsep]
4.1.8.2. Rollback
- Rollback can only be used to cancel proposed changes;
there is no support for rolling back from a stable state to a
previous stable state
*/
promise_test(t => {
const pc = new RTCPeerConnection();
t.add_cleanup(() => pc.close());
return promise_rejects(t, 'InvalidStateError',
pc.setRemoteDescription({ type: 'rollback' }));
}, `setRemoteDescription(rollback) from stable state should reject with InvalidStateError`);
promise_test(t => {
const pc = new RTCPeerConnection();
t.add_cleanup(() => pc.close());
return generateAudioReceiveOnlyOffer(pc)
.then(offer => pc.setRemoteDescription(offer))
.then(() => pc.setRemoteDescription({
type: 'rollback',
sdp: '!<Invalid SDP Content>;'
}));
}, `setRemoteDescription(rollback) should ignore invalid sdp content and succeed`);
promise_test(async t => {
const pc1 = new RTCPeerConnection();
const pc2 = new RTCPeerConnection();
t.add_cleanup(() => pc1.close());
t.add_cleanup(() => pc2.close());
// We don't use this right away
const offer1 = await pc1.createOffer({offerToReceiveAudio: true});
// Create offer from pc2, apply and rollback on pc1
const offer2 = await pc2.createOffer({offerToReceiveAudio: true});
await pc1.setRemoteDescription(offer2);
await pc1.setRemoteDescription({type: "rollback"});
// Then try applying pc1's old offer
await pc1.setLocalDescription(offer1);
}, `local offer created before setRemoteDescription(remote offer) then rollback should still be usable`);
promise_test(async t => {
const pc1 = new RTCPeerConnection();
const pc2 = new RTCPeerConnection();
t.add_cleanup(() => pc1.close());
t.add_cleanup(() => pc2.close());
const stream = await getNoiseStream({ video: true });
t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
pc1.addTrack(stream.getTracks()[0], stream);
// We don't use this right away. pc1 has provisionally decided that the
// (only) transceiver is bound to level 0.
const offer1 = await pc1.createOffer();
// Create offer from pc2, apply and rollback on pc1
const offer2 = await pc2.createOffer({offerToReceiveAudio: true, offerToReceiveVideo: true});
// pc1 now should change its mind about what level its video transceiver is
// bound to. It was 0, now it is 1.
await pc1.setRemoteDescription(offer2);
// Rolling back should put things back the way they were.
await pc1.setRemoteDescription({type: "rollback"});
// Then try applying pc1's old offer
await pc1.setLocalDescription(offer1);
}, "local offer created before setRemoteDescription(remote offer) with different transceiver level assignments then rollback should still be usable");
</script>
|