| Soares Chen | 363f125 | 2017-07-10 09:22:15 | [diff] [blame] | 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: |
| youennf | cd17e3b | 2018-11-04 08:03:59 | [diff] [blame] | 14 | // generateDataChannelOffer |
| Jan-Ivar Bruaroey | 6d3fda5 | 2018-07-04 13:49:09 | [diff] [blame] | 15 | // assert_session_desc_not_similar |
| 16 | // assert_session_desc_similar |
| Soares Chen | 363f125 | 2017-07-10 09:22:15 | [diff] [blame] | 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 |
| Soares Chen | 25d8bf3 | 2017-07-20 03:08:41 | [diff] [blame] | 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. |
| Soares Chen | 363f125 | 2017-07-10 09:22:15 | [diff] [blame] | 57 | */ |
| 58 | promise_test(t => { |
| 59 | const pc = new RTCPeerConnection(); |
| Philipp Hancke | 1622a02 | 2018-06-11 10:00:53 | [diff] [blame] | 60 | t.add_cleanup(() => pc.close()); |
| Philipp Hancke | eb37a99 | 2018-05-30 15:55:02 | [diff] [blame] | 61 | |
| 62 | const states = []; |
| 63 | pc.addEventListener('signalingstatechange', () => states.push(pc.signalingState)); |
| Soares Chen | 363f125 | 2017-07-10 09:22:15 | [diff] [blame] | 64 | |
| Youenn Fablet | 7b16358 | 2018-11-04 07:30:59 | [diff] [blame] | 65 | return generateAudioReceiveOnlyOffer(pc) |
| Soares Chen | 363f125 | 2017-07-10 09:22:15 | [diff] [blame] | 66 | .then(offer => |
| 67 | pc.setLocalDescription(offer) |
| 68 | .then(() => { |
| 69 | assert_equals(pc.signalingState, 'have-local-offer'); |
| Jan-Ivar Bruaroey | 6d3fda5 | 2018-07-04 13:49:09 | [diff] [blame] | 70 | assert_session_desc_similar(pc.localDescription, offer); |
| 71 | assert_session_desc_similar(pc.pendingLocalDescription, offer); |
| Soares Chen | 363f125 | 2017-07-10 09:22:15 | [diff] [blame] | 72 | assert_equals(pc.currentLocalDescription, null); |
| Philipp Hancke | eb37a99 | 2018-05-30 15:55:02 | [diff] [blame] | 73 | |
| 74 | assert_array_equals(states, ['have-local-offer']); |
| Soares Chen | 363f125 | 2017-07-10 09:22:15 | [diff] [blame] | 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(); |
| Philipp Hancke | 1622a02 | 2018-06-11 10:00:53 | [diff] [blame] | 86 | t.add_cleanup(() => pc.close()); |
| Youenn Fablet | 7b16358 | 2018-11-04 07:30:59 | [diff] [blame] | 87 | return generateAudioReceiveOnlyOffer(pc) |
| Soares Chen | 363f125 | 2017-07-10 09:22:15 | [diff] [blame] | 88 | .then(offer => |
| 89 | pc.setLocalDescription({ type: 'offer' }) |
| 90 | .then(() => { |
| 91 | assert_equals(pc.signalingState, 'have-local-offer'); |
| Jan-Ivar Bruaroey | 6d3fda5 | 2018-07-04 13:49:09 | [diff] [blame] | 92 | assert_session_desc_similar(pc.localDescription, offer); |
| 93 | assert_session_desc_similar(pc.pendingLocalDescription, offer); |
| Soares Chen | 363f125 | 2017-07-10 09:22:15 | [diff] [blame] | 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(); |
| Philipp Hancke | 1622a02 | 2018-06-11 10:00:53 | [diff] [blame] | 107 | t.add_cleanup(() => pc.close()); |
| Harald Alvestrand | e48a42f | 2018-03-22 18:59:43 | [diff] [blame] | 108 | const pc2 = new RTCPeerConnection(); |
| Soares Chen | 363f125 | 2017-07-10 09:22:15 | [diff] [blame] | 109 | |
| Philipp Hancke | 1622a02 | 2018-06-11 10:00:53 | [diff] [blame] | 110 | t.add_cleanup(() => pc2.close()); |
| 111 | |
| youennf | cd17e3b | 2018-11-04 08:03:59 | [diff] [blame] | 112 | return generateDataChannelOffer(pc) |
| Soares Chen | 363f125 | 2017-07-10 09:22:15 | [diff] [blame] | 113 | .then(offer => |
| 114 | promise_rejects(t, 'InvalidModificationError', |
| Harald Alvestrand | e48a42f | 2018-03-22 18:59:43 | [diff] [blame] | 115 | pc2.setLocalDescription(offer))); |
| Soares Chen | 363f125 | 2017-07-10 09:22:15 | [diff] [blame] | 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(); |
| Philipp Hancke | 1622a02 | 2018-06-11 10:00:53 | [diff] [blame] | 124 | t.add_cleanup(() => pc.close()); |
| Youenn Fablet | 7b16358 | 2018-11-04 07:30:59 | [diff] [blame] | 125 | return generateAudioReceiveOnlyOffer(pc) |
| Soares Chen | 363f125 | 2017-07-10 09:22:15 | [diff] [blame] | 126 | .then(offer1 => |
| Youenn Fablet | 7b16358 | 2018-11-04 07:30:59 | [diff] [blame] | 127 | generateVideoReceiveOnlyOffer(pc) |
| Soares Chen | 363f125 | 2017-07-10 09:22:15 | [diff] [blame] | 128 | .then(offer2 => { |
| Jan-Ivar Bruaroey | 6d3fda5 | 2018-07-04 13:49:09 | [diff] [blame] | 129 | assert_session_desc_not_similar(offer1, offer2); |
| Soares Chen | 363f125 | 2017-07-10 09:22:15 | [diff] [blame] | 130 | return promise_rejects(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(); |
| Philipp Hancke | 1622a02 | 2018-06-11 10:00:53 | [diff] [blame] | 137 | t.add_cleanup(() => pc.close()); |
| Soares Chen | 363f125 | 2017-07-10 09:22:15 | [diff] [blame] | 138 | |
| Philipp Hancke | eb37a99 | 2018-05-30 15:55:02 | [diff] [blame] | 139 | const states = []; |
| 140 | pc.addEventListener('signalingstatechange', () => states.push(pc.signalingState)); |
| Soares Chen | 363f125 | 2017-07-10 09:22:15 | [diff] [blame] | 141 | |
| Youenn Fablet | 7b16358 | 2018-11-04 07:30:59 | [diff] [blame] | 142 | return generateAudioReceiveOnlyOffer(pc) |
| Soares Chen | 363f125 | 2017-07-10 09:22:15 | [diff] [blame] | 143 | .then(offer1 => |
| 144 | pc.setLocalDescription(offer1) |
| 145 | .then(() => |
| Youenn Fablet | 7b16358 | 2018-11-04 07:30:59 | [diff] [blame] | 146 | generateVideoReceiveOnlyOffer(pc) |
| Soares Chen | 363f125 | 2017-07-10 09:22:15 | [diff] [blame] | 147 | .then(offer2 => |
| 148 | pc.setLocalDescription(offer2) |
| Mark Roberts | bfa6d94 | 2018-11-08 05:55:00 | [diff] [blame] | 149 | .then(() => { |
| Jan-Ivar Bruaroey | 6d3fda5 | 2018-07-04 13:49:09 | [diff] [blame] | 150 | assert_session_desc_not_similar(offer1, offer2); |
| Soares Chen | 363f125 | 2017-07-10 09:22:15 | [diff] [blame] | 151 | assert_equals(pc.signalingState, 'have-local-offer'); |
| Jan-Ivar Bruaroey | 6d3fda5 | 2018-07-04 13:49:09 | [diff] [blame] | 152 | assert_session_desc_similar(pc.localDescription, offer2); |
| 153 | assert_session_desc_similar(pc.pendingLocalDescription, offer2); |
| Soares Chen | 363f125 | 2017-07-10 09:22:15 | [diff] [blame] | 154 | assert_equals(pc.currentLocalDescription, null); |
| Philipp Hancke | eb37a99 | 2018-05-30 15:55:02 | [diff] [blame] | 155 | |
| 156 | assert_array_equals(states, ['have-local-offer']); |
| Soares Chen | 363f125 | 2017-07-10 09:22:15 | [diff] [blame] | 157 | })))); |
| 158 | }, 'Creating and setting offer multiple times should succeed'); |
| 159 | |
| Byron Campen [:bwc] | 65f12c6 | 2019-03-23 17:05:00 | [diff] [blame] | 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 | const offer = await pc1.createOffer({offerToReceiveAudio: true}); // [[LastOffer]] set |
| 167 | const offer2 = await pc2.createOffer({offerToReceiveVideo: true}); |
| 168 | await pc1.setRemoteDescription(offer2); |
| 169 | await pc1.createAnswer(); // [[LastAnswer]] set |
| 170 | await pc1.setRemoteDescription({type: "rollback"}); |
| 171 | await pc1.setLocalDescription(offer); |
| 172 | }, "Setting previously generated offer after a call to createAnswer should work"); |
| 173 | |
| Soares Chen | 363f125 | 2017-07-10 09:22:15 | [diff] [blame] | 174 | </script> |