| foolip | 72525e9 | 2017-04-24 09:31:03 | [diff] [blame] | 1 | <!doctype html> |
| 2 | <meta charset=utf-8> |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 3 | <title>RTCPeerConnection.prototype.createDataChannel</title> |
| foolip | 72525e9 | 2017-04-24 09:31:03 | [diff] [blame] | 4 | <script src=/resources/testharness.js></script> |
| 5 | <script src=/resources/testharnessreport.js></script> |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 6 | <script src="RTCPeerConnection-helper.js"></script> |
| foolip | 72525e9 | 2017-04-24 09:31:03 | [diff] [blame] | 7 | <script> |
| 8 | 'use strict'; |
| 9 | |
| moz-wptsync-bot | b0d2aa5 | 2019-10-02 14:44:10 | [diff] [blame] | 10 | const stopTracks = (...streams) => { |
| 11 | streams.forEach(stream => stream.getTracks().forEach(track => track.stop())); |
| 12 | }; |
| 13 | |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 14 | // Test is based on the following revision: |
| 15 | // https://rawgit.com/w3c/webrtc-pc/1cc5bfc3ff18741033d804c4a71f7891242fb5b3/webrtc.html |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 16 | |
| 17 | /* |
| 18 | 6.1. RTCPeerConnection Interface Extensions |
| 19 | |
| 20 | partial interface RTCPeerConnection { |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 21 | [...] |
| 22 | RTCDataChannel createDataChannel(USVString label, |
| 23 | optional RTCDataChannelInit dataChannelDict); |
| 24 | [...] |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 25 | }; |
| 26 | |
| 27 | 6.2. RTCDataChannel |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 28 | |
| 29 | interface RTCDataChannel : EventTarget { |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 30 | readonly attribute USVString label; |
| 31 | readonly attribute boolean ordered; |
| 32 | readonly attribute unsigned short? maxPacketLifeTime; |
| 33 | readonly attribute unsigned short? maxRetransmits; |
| 34 | readonly attribute USVString protocol; |
| 35 | readonly attribute boolean negotiated; |
| 36 | readonly attribute unsigned short? id; |
| 37 | readonly attribute RTCPriorityType priority; |
| 38 | readonly attribute RTCDataChannelState readyState; |
| 39 | readonly attribute unsigned long bufferedAmount; |
| 40 | attribute unsigned long bufferedAmountLowThreshold; |
| 41 | [...] |
| 42 | attribute DOMString binaryType; |
| 43 | [...] |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 44 | }; |
| 45 | |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 46 | dictionary RTCDataChannelInit { |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 47 | boolean ordered = true; |
| 48 | unsigned short maxPacketLifeTime; |
| 49 | unsigned short maxRetransmits; |
| 50 | USVString protocol = ""; |
| 51 | boolean negotiated = false; |
| 52 | [EnforceRange] |
| 53 | unsigned short id; |
| 54 | RTCPriorityType priority = "low"; |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 55 | }; |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 56 | |
| 57 | 4.9.1. RTCPriorityType Enum |
| 58 | |
| 59 | enum RTCPriorityType { |
| 60 | "very-low", |
| 61 | "low", |
| 62 | "medium", |
| 63 | "high" |
| 64 | }; |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 65 | */ |
| foolip | 72525e9 | 2017-04-24 09:31:03 | [diff] [blame] | 66 | |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 67 | test(t => { |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 68 | const pc = new RTCPeerConnection(); |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 69 | t.add_cleanup(() => pc.close()); |
| 70 | |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 71 | assert_equals(pc.createDataChannel.length, 1); |
| Boris Zbarsky | 71bf020 | 2020-01-21 04:26:37 | [diff] [blame^] | 72 | assert_throws_js(TypeError, () => pc.createDataChannel()); |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 73 | }, 'createDataChannel with no argument should throw TypeError'); |
| 74 | |
| 75 | /* |
| 76 | 6.2. createDataChannel |
| 77 | 2. If connection's [[isClosed]] slot is true, throw an InvalidStateError. |
| 78 | */ |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 79 | test(t => { |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 80 | const pc = new RTCPeerConnection(); |
| foolip | 72525e9 | 2017-04-24 09:31:03 | [diff] [blame] | 81 | pc.close(); |
| 82 | assert_equals(pc.signalingState, 'closed', 'signaling state'); |
| 83 | assert_throws('InvalidStateError', () => pc.createDataChannel('')); |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 84 | }, 'createDataChannel with closed connection should throw InvalidStateError'); |
| foolip | 72525e9 | 2017-04-24 09:31:03 | [diff] [blame] | 85 | |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 86 | /* |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 87 | 6.1. createDataChannel |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 88 | 4. Let channel have a [[DataChannelLabel]] internal slot initialized to the value of the |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 89 | first argument. |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 90 | 6. Let options be the second argument. |
| 91 | 7. Let channel have an [[MaxPacketLifeTime]] internal slot initialized to |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 92 | option's maxPacketLifeTime member, if present, otherwise null. |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 93 | 8. Let channel have a [[ReadyState]] internal slot initialized to "connecting". |
| 94 | 9. Let channel have a [[BufferedAmount]] internal slot initialized to 0. |
| 95 | 10. Let channel have an [[MaxRetransmits]] internal slot initialized to |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 96 | option's maxRetransmits member, if present, otherwise null. |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 97 | 11. Let channel have an [[Ordered]] internal slot initialized to option's |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 98 | ordered member. |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 99 | 12. Let channel have a [[DataChannelProtocol]] internal slot initialized to option's |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 100 | protocol member. |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 101 | 14. Let channel have a [[Negotiated]] internal slot initialized to option's negotiated |
| 102 | member. |
| 103 | 15. Let channel have an [[DataChannelId]] internal slot initialized to option's id |
| 104 | member, if it is present and [[Negotiated]] is true, otherwise null. |
| 105 | 17. Let channel have an [[DataChannelPriority]] internal slot initialized to option's |
| 106 | priority member. |
| 107 | 21. If the [[DataChannelId]] slot is null (due to no ID being passed into |
| 108 | createDataChannel, or [[Negotiated]] being false), and the DTLS role of the SCTP |
| 109 | transport has already been negotiated, then initialize [[DataChannelId]] to a value |
| 110 | generated by the user agent, according to [RTCWEB-DATA-PROTOCOL], and skip |
| 111 | to the next step. If no available ID could be generated, or if the value of the |
| 112 | [[DataChannelId]] slot is being used by an existing RTCDataChannel, throw an |
| 113 | OperationError exception. |
| 114 | |
| 115 | Note |
| 116 | If the [[DataChannelId]] slot is null after this step, it will be populated once |
| 117 | the DTLS role is determined during the process of setting an RTCSessionDescription. |
| 118 | 22. If channel is the first RTCDataChannel created on connection, update the |
| 119 | negotiation-needed flag for connection. |
| 120 | |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 121 | |
| 122 | 6.2. RTCDataChannel |
| 123 | |
| 124 | A RTCDataChannel, created with createDataChannel or dispatched via a |
| 125 | RTCDataChannelEvent, MUST initially be in the connecting state |
| 126 | |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 127 | bufferedAmountLowThreshold |
| 128 | [...] The bufferedAmountLowThreshold is initially zero on each new RTCDataChannel, |
| 129 | but the application may change its value at any time. |
| 130 | |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 131 | binaryType |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 132 | [...] When a RTCDataChannel object is created, the binaryType attribute MUST |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 133 | be initialized to the string "blob". |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 134 | */ |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 135 | test(t => { |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 136 | const pc = new RTCPeerConnection(); |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 137 | t.add_cleanup(() => pc.close()); |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 138 | |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 139 | const dc = pc.createDataChannel(''); |
| 140 | |
| 141 | assert_true(dc instanceof RTCDataChannel, 'is RTCDataChannel'); |
| 142 | assert_equals(dc.label, ''); |
| 143 | assert_equals(dc.ordered, true); |
| 144 | assert_equals(dc.maxPacketLifeTime, null); |
| 145 | assert_equals(dc.maxRetransmits, null); |
| 146 | assert_equals(dc.protocol, ''); |
| 147 | assert_equals(dc.negotiated, false); |
| Taylor Brandstetter | ce56f0e | 2017-05-09 13:53:14 | [diff] [blame] | 148 | // Since no offer/answer exchange has occurred yet, the DTLS role is unknown |
| 149 | // and so the ID should be null. |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 150 | assert_equals(dc.id, null); |
| 151 | assert_equals(dc.priority, 'low'); |
| 152 | assert_equals(dc.readyState, 'connecting'); |
| 153 | assert_equals(dc.bufferedAmount, 0); |
| 154 | assert_equals(dc.bufferedAmountLowThreshold, 0); |
| 155 | assert_equals(dc.binaryType, 'blob'); |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 156 | }, 'createDataChannel attribute default values'); |
| foolip | 72525e9 | 2017-04-24 09:31:03 | [diff] [blame] | 157 | |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 158 | test(t => { |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 159 | const pc = new RTCPeerConnection(); |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 160 | t.add_cleanup(() => pc.close()); |
| 161 | |
| 162 | const dc = pc.createDataChannel('test', { |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 163 | ordered: false, |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 164 | maxRetransmits: 1, |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 165 | // Note: maxPacketLifeTime is not set in this test. |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 166 | protocol: 'custom', |
| 167 | negotiated: true, |
| 168 | id: 3, |
| 169 | priority: 'high' |
| 170 | }); |
| 171 | |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 172 | assert_true(dc instanceof RTCDataChannel, 'is RTCDataChannel'); |
| 173 | assert_equals(dc.label, 'test'); |
| 174 | assert_equals(dc.ordered, false); |
| 175 | assert_equals(dc.maxPacketLifeTime, null); |
| 176 | assert_equals(dc.maxRetransmits, 1); |
| 177 | assert_equals(dc.protocol, 'custom'); |
| 178 | assert_equals(dc.negotiated, true); |
| 179 | assert_equals(dc.id, 3); |
| 180 | assert_equals(dc.priority, 'high'); |
| 181 | assert_equals(dc.readyState, 'connecting'); |
| 182 | assert_equals(dc.bufferedAmount, 0); |
| 183 | assert_equals(dc.bufferedAmountLowThreshold, 0); |
| 184 | assert_equals(dc.binaryType, 'blob'); |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 185 | |
| 186 | const dc2 = pc.createDataChannel('test2', { |
| 187 | ordered: false, |
| 188 | maxPacketLifeTime: 42 |
| 189 | }); |
| 190 | assert_equals(dc2.label, 'test2'); |
| 191 | assert_equals(dc2.maxPacketLifeTime, 42); |
| 192 | assert_equals(dc.maxRetransmits, null); |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 193 | }, 'createDataChannel with provided parameters should initialize attributes to provided values'); |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 194 | |
| 195 | /* |
| 196 | 6.2. createDataChannel |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 197 | 4. Let channel have a [[DataChannelLabel]] internal slot initialized to the value of the |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 198 | first argument. |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 199 | |
| 200 | [ECMA262] 7.1.12. ToString(argument) |
| 201 | undefined -> "undefined" |
| 202 | null -> "null" |
| 203 | |
| 204 | [WebIDL] 3.10.15. Convert a DOMString to a sequence of Unicode scalar values |
| 205 | */ |
| foolip | 72525e9 | 2017-04-24 09:31:03 | [diff] [blame] | 206 | const labels = [ |
| 207 | ['"foo"', 'foo', 'foo'], |
| 208 | ['null', null, 'null'], |
| 209 | ['undefined', undefined, 'undefined'], |
| 210 | ['lone surrogate', '\uD800', '\uFFFD'], |
| 211 | ]; |
| 212 | for (const [description, label, expected] of labels) { |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 213 | test(t => { |
| foolip | 72525e9 | 2017-04-24 09:31:03 | [diff] [blame] | 214 | const pc = new RTCPeerConnection; |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 215 | t.add_cleanup(() => pc.close()); |
| 216 | |
| 217 | const dc = pc.createDataChannel(label); |
| 218 | assert_equals(dc.label, expected); |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 219 | }, `createDataChannel with label ${description} should succeed`); |
| foolip | 72525e9 | 2017-04-24 09:31:03 | [diff] [blame] | 220 | } |
| 221 | |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 222 | /* |
| 223 | 6.2. RTCDataChannel |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 224 | createDataChannel |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 225 | 11. Let channel have an [[Ordered]] internal slot initialized to option's |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 226 | ordered member. |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 227 | */ |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 228 | test(t => { |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 229 | const pc = new RTCPeerConnection(); |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 230 | t.add_cleanup(() => pc.close()); |
| 231 | |
| 232 | const dc = pc.createDataChannel('', { ordered: false }); |
| 233 | assert_equals(dc.ordered, false); |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 234 | }, 'createDataChannel with ordered false should succeed'); |
| foolip | 72525e9 | 2017-04-24 09:31:03 | [diff] [blame] | 235 | |
| 236 | // true as the default value of a boolean is confusing because null is converted |
| 237 | // to false while undefined is converted to true. |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 238 | test(t => { |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 239 | const pc = new RTCPeerConnection(); |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 240 | t.add_cleanup(() => pc.close()); |
| 241 | |
| 242 | const dc1 = pc.createDataChannel('', { ordered: null }); |
| 243 | assert_equals(dc1.ordered, false); |
| 244 | const dc2 = pc.createDataChannel('', { ordered: undefined }); |
| 245 | assert_equals(dc2.ordered, true); |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 246 | }, 'createDataChannel with ordered null/undefined should succeed'); |
| foolip | 72525e9 | 2017-04-24 09:31:03 | [diff] [blame] | 247 | |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 248 | /* |
| 249 | 6.2. RTCDataChannel |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 250 | createDataChannel |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 251 | 7. Let channel have an [[MaxPacketLifeTime]] internal slot initialized to |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 252 | option's maxPacketLifeTime member, if present, otherwise null. |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 253 | */ |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 254 | test(t => { |
| foolip | 72525e9 | 2017-04-24 09:31:03 | [diff] [blame] | 255 | const pc = new RTCPeerConnection; |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 256 | t.add_cleanup(() => pc.close()); |
| 257 | |
| 258 | const dc = pc.createDataChannel('', { maxPacketLifeTime: 0 }); |
| 259 | assert_equals(dc.maxPacketLifeTime, 0); |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 260 | }, 'createDataChannel with maxPacketLifeTime 0 should succeed'); |
| foolip | 72525e9 | 2017-04-24 09:31:03 | [diff] [blame] | 261 | |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 262 | /* |
| 263 | 6.2. RTCDataChannel |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 264 | createDataChannel |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 265 | 10. Let channel have an [[MaxRetransmits]] internal slot initialized to |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 266 | option's maxRetransmits member, if present, otherwise null. |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 267 | */ |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 268 | test(t => { |
| foolip | 72525e9 | 2017-04-24 09:31:03 | [diff] [blame] | 269 | const pc = new RTCPeerConnection; |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 270 | t.add_cleanup(() => pc.close()); |
| 271 | |
| 272 | const dc = pc.createDataChannel('', { maxRetransmits: 0 }); |
| 273 | assert_equals(dc.maxRetransmits, 0); |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 274 | }, 'createDataChannel with maxRetransmits 0 should succeed'); |
| foolip | 72525e9 | 2017-04-24 09:31:03 | [diff] [blame] | 275 | |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 276 | /* |
| 277 | 6.2. createDataChannel |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 278 | 18. If both [[MaxPacketLifeTime]] and [[MaxRetransmits]] attributes are set (not null), |
| 279 | throw a TypeError. |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 280 | */ |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 281 | test(t => { |
| foolip | 72525e9 | 2017-04-24 09:31:03 | [diff] [blame] | 282 | const pc = new RTCPeerConnection; |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 283 | t.add_cleanup(() => pc.close()); |
| 284 | |
| 285 | pc.createDataChannel('', { |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 286 | maxPacketLifeTime: undefined, |
| 287 | maxRetransmits: undefined |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 288 | }); |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 289 | }, 'createDataChannel with both maxPacketLifeTime and maxRetransmits undefined should succeed'); |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 290 | |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 291 | test(t => { |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 292 | const pc = new RTCPeerConnection; |
| 293 | t.add_cleanup(() => pc.close()); |
| 294 | |
| Boris Zbarsky | 71bf020 | 2020-01-21 04:26:37 | [diff] [blame^] | 295 | assert_throws_js(TypeError, () => pc.createDataChannel('', { |
| foolip | 72525e9 | 2017-04-24 09:31:03 | [diff] [blame] | 296 | maxPacketLifeTime: 0, |
| 297 | maxRetransmits: 0 |
| 298 | })); |
| Boris Zbarsky | 71bf020 | 2020-01-21 04:26:37 | [diff] [blame^] | 299 | assert_throws_js(TypeError, () => pc.createDataChannel('', { |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 300 | maxPacketLifeTime: 42, |
| 301 | maxRetransmits: 42 |
| 302 | })); |
| 303 | }, 'createDataChannel with both maxPacketLifeTime and maxRetransmits should throw TypeError'); |
| foolip | 72525e9 | 2017-04-24 09:31:03 | [diff] [blame] | 304 | |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 305 | /* |
| 306 | 6.2. RTCDataChannel |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 307 | createDataChannel |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 308 | 12. Let channel have a [[DataChannelProtocol]] internal slot initialized to option's |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 309 | protocol member. |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 310 | */ |
| foolip | 72525e9 | 2017-04-24 09:31:03 | [diff] [blame] | 311 | const protocols = [ |
| 312 | ['"foo"', 'foo', 'foo'], |
| 313 | ['null', null, 'null'], |
| 314 | ['undefined', undefined, ''], |
| 315 | ['lone surrogate', '\uD800', '\uFFFD'], |
| 316 | ]; |
| 317 | for (const [description, protocol, expected] of protocols) { |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 318 | test(t => { |
| foolip | 72525e9 | 2017-04-24 09:31:03 | [diff] [blame] | 319 | const pc = new RTCPeerConnection; |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 320 | t.add_cleanup(() => pc.close()); |
| 321 | |
| 322 | const dc = pc.createDataChannel('', { protocol }); |
| 323 | assert_equals(dc.protocol, expected); |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 324 | }, `createDataChannel with protocol ${description} should succeed`); |
| foolip | 72525e9 | 2017-04-24 09:31:03 | [diff] [blame] | 325 | } |
| 326 | |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 327 | /* |
| 328 | 6.2. RTCDataChannel |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 329 | createDataChannel |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 330 | 20. If [[DataChannelId]] is equal to 65535, which is greater than the maximum allowed |
| 331 | ID of 65534 but still qualifies as an unsigned short, throw a TypeError. |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 332 | */ |
| Boris Zbarsky | f1d2a71 | 2020-01-21 04:24:46 | [diff] [blame] | 333 | for (const id of [0, 1, 65534, 65535]) { |
| 334 | test((t) => { |
| 335 | const pc = new RTCPeerConnection(); |
| 336 | t.add_cleanup(() => pc.close()); |
| 337 | const dc = pc.createDataChannel('', { id }); |
| 338 | assert_equals(dc.id, null); |
| 339 | }, `createDataChannel with id ${id} and negotiated not set should succeed, but not set the channel's id`); |
| 340 | } |
| 341 | |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 342 | for (const id of [0, 1, 65534]) { |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 343 | test(t => { |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 344 | const pc = new RTCPeerConnection(); |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 345 | t.add_cleanup(() => pc.close()); |
| 346 | |
| Harald Alvestrand | 267d5dd | 2019-04-02 12:40:16 | [diff] [blame] | 347 | const dc = pc.createDataChannel('', { 'negotiated': true, 'id': id }); |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 348 | assert_equals(dc.id, id); |
| Boris Zbarsky | f1d2a71 | 2020-01-21 04:24:46 | [diff] [blame] | 349 | }, `createDataChannel with id ${id} and negotiated true should succeed, and set the channel's id`); |
| 350 | } |
| 351 | |
| 352 | for (const id of [-1, 65536]) { |
| 353 | test((t) => { |
| 354 | const pc = new RTCPeerConnection(); |
| 355 | t.add_cleanup(() => pc.close()); |
| Boris Zbarsky | 71bf020 | 2020-01-21 04:26:37 | [diff] [blame^] | 356 | assert_throws_js(TypeError, () => pc.createDataChannel('', { id })); |
| Boris Zbarsky | f1d2a71 | 2020-01-21 04:24:46 | [diff] [blame] | 357 | }, `createDataChannel with id ${id} and negotiated not set should throw TypeError`); |
| foolip | 72525e9 | 2017-04-24 09:31:03 | [diff] [blame] | 358 | } |
| 359 | |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 360 | for (const id of [-1, 65535, 65536]) { |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 361 | test(t => { |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 362 | const pc = new RTCPeerConnection(); |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 363 | t.add_cleanup(() => pc.close()); |
| 364 | |
| Boris Zbarsky | 71bf020 | 2020-01-21 04:26:37 | [diff] [blame^] | 365 | assert_throws_js(TypeError, () => pc.createDataChannel('', |
| Harald Alvestrand | 267d5dd | 2019-04-02 12:40:16 | [diff] [blame] | 366 | { 'negotiated': true, 'id': id })); |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 367 | }, `createDataChannel with id ${id} should throw TypeError`); |
| 368 | } |
| 369 | |
| 370 | /* |
| 371 | 6.2. RTCDataChannel |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 372 | createDataChannel |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 373 | 17. Let channel have an [[DataChannelPriority]] internal slot initialized to option's |
| 374 | priority member. |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 375 | */ |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 376 | test(t => { |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 377 | const pc = new RTCPeerConnection(); |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 378 | t.add_cleanup(() => pc.close()); |
| 379 | |
| 380 | const dc = pc.createDataChannel('', { priority: 'high' }); |
| 381 | assert_equals(dc.priority, 'high'); |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 382 | }, 'createDataChannel with priority "high" should succeed'); |
| 383 | |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 384 | test(t => { |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 385 | const pc = new RTCPeerConnection(); |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 386 | t.add_cleanup(() => pc.close()); |
| 387 | |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 388 | assert_throws(new TypeError(), |
| 389 | () => pc.createDataChannel('', { priority: 'invalid' })); |
| 390 | }, 'createDataChannel with invalid priority should throw TypeError'); |
| 391 | |
| 392 | /* |
| 393 | 6.2. createDataChannel |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 394 | 5. If [[DataChannelLabel]] is longer than 65535 bytes, throw a TypeError. |
| 395 | */ |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 396 | test(t => { |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 397 | const pc = new RTCPeerConnection(); |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 398 | t.add_cleanup(() => pc.close()); |
| 399 | |
| Boris Zbarsky | 71bf020 | 2020-01-21 04:26:37 | [diff] [blame^] | 400 | assert_throws_js(TypeError, () => |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 401 | pc.createDataChannel('l'.repeat(65536))); |
| 402 | |
| Boris Zbarsky | 71bf020 | 2020-01-21 04:26:37 | [diff] [blame^] | 403 | assert_throws_js(TypeError, () => |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 404 | pc.createDataChannel('l'.repeat(65536), { |
| 405 | negotiated: true, |
| 406 | id: 42 |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 407 | })); |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 408 | }, 'createDataChannel with too long label should throw TypeError'); |
| 409 | |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 410 | test(t => { |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 411 | const pc = new RTCPeerConnection(); |
| 412 | t.add_cleanup(() => pc.close()); |
| 413 | |
| Boris Zbarsky | 71bf020 | 2020-01-21 04:26:37 | [diff] [blame^] | 414 | assert_throws_js(TypeError, () => |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 415 | pc.createDataChannel('\u00b5'.repeat(32768))); |
| 416 | |
| Boris Zbarsky | 71bf020 | 2020-01-21 04:26:37 | [diff] [blame^] | 417 | assert_throws_js(TypeError, () => |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 418 | pc.createDataChannel('\u00b5'.repeat(32768), { |
| 419 | negotiated: true, |
| 420 | id: 42 |
| 421 | })); |
| 422 | }, 'createDataChannel with too long label (2 byte unicode) should throw TypeError'); |
| 423 | |
| 424 | /* |
| 425 | 6.2. label |
| 426 | [...] Scripts are allowed to create multiple RTCDataChannel objects with the same label. |
| 427 | [...] |
| 428 | */ |
| 429 | test(t => { |
| 430 | const pc = new RTCPeerConnection(); |
| 431 | t.add_cleanup(() => pc.close()); |
| 432 | |
| 433 | const label = 'test'; |
| 434 | |
| 435 | pc.createDataChannel(label); |
| 436 | pc.createDataChannel(label); |
| 437 | }, 'createDataChannel with same label used twice should not throw'); |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 438 | |
| 439 | /* |
| 440 | 6.2. createDataChannel |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 441 | 13. If [[DataChannelProtocol]] is longer than 65535 bytes long, throw a TypeError. |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 442 | */ |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 443 | |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 444 | test(t => { |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 445 | const pc = new RTCPeerConnection; |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 446 | t.add_cleanup(() => pc.close()); |
| 447 | const channel = pc.createDataChannel('', { negotiated: true, id: 42 }); |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 448 | assert_equals(channel.negotiated, true); |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 449 | }, 'createDataChannel with negotiated true and id should succeed'); |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 450 | |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 451 | test(t => { |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 452 | const pc = new RTCPeerConnection(); |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 453 | t.add_cleanup(() => pc.close()); |
| 454 | |
| Boris Zbarsky | 71bf020 | 2020-01-21 04:26:37 | [diff] [blame^] | 455 | assert_throws_js(TypeError, () => |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 456 | pc.createDataChannel('', { |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 457 | protocol: 'p'.repeat(65536) |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 458 | })); |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 459 | |
| Boris Zbarsky | 71bf020 | 2020-01-21 04:26:37 | [diff] [blame^] | 460 | assert_throws_js(TypeError, () => |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 461 | pc.createDataChannel('', { |
| 462 | protocol: 'p'.repeat(65536), |
| 463 | negotiated: true, |
| 464 | id: 42 |
| 465 | })); |
| 466 | }, 'createDataChannel with too long protocol should throw TypeError'); |
| 467 | |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 468 | test(t => { |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 469 | const pc = new RTCPeerConnection(); |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 470 | t.add_cleanup(() => pc.close()); |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 471 | |
| Boris Zbarsky | 71bf020 | 2020-01-21 04:26:37 | [diff] [blame^] | 472 | assert_throws_js(TypeError, () => |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 473 | pc.createDataChannel('', { |
| 474 | protocol: '\u00b6'.repeat(32768) |
| 475 | })); |
| 476 | |
| Boris Zbarsky | 71bf020 | 2020-01-21 04:26:37 | [diff] [blame^] | 477 | assert_throws_js(TypeError, () => |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 478 | pc.createDataChannel('', { |
| 479 | protocol: '\u00b6'.repeat(32768), |
| 480 | negotiated: true, |
| 481 | id: 42 |
| 482 | })); |
| 483 | }, 'createDataChannel with too long protocol (2 byte unicode) should throw TypeError'); |
| 484 | |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 485 | test(t => { |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 486 | const pc = new RTCPeerConnection(); |
| 487 | t.add_cleanup(() => pc.close()); |
| 488 | |
| 489 | const label = 'l'.repeat(65535); |
| 490 | const protocol = 'p'.repeat(65535); |
| 491 | |
| 492 | const dc = pc.createDataChannel(label, { |
| 493 | protocol: protocol |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 494 | }); |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 495 | |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 496 | assert_equals(dc.label, label); |
| 497 | assert_equals(dc.protocol, protocol); |
| 498 | }, 'createDataChannel with maximum length label and protocol should succeed'); |
| 499 | |
| 500 | /* |
| 501 | 6.2 createDataChannel |
| 502 | 15. Let channel have an [[DataChannelId]] internal slot initialized to option's id member, |
| 503 | if it is present and [[Negotiated]] is true, otherwise null. |
| 504 | |
| 505 | NOTE |
| 506 | This means the id member will be ignored if the data channel is negotiated in-band; this |
| 507 | is intentional. Data channels negotiated in-band should have IDs selected based on the |
| 508 | DTLS role, as specified in [RTCWEB-DATA-PROTOCOL]. |
| 509 | */ |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 510 | test(t => { |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 511 | const pc = new RTCPeerConnection; |
| 512 | t.add_cleanup(() => pc.close()); |
| 513 | |
| 514 | const dc = pc.createDataChannel('', { |
| 515 | negotiated: false, |
| 516 | }); |
| 517 | assert_equals(dc.negotiated, false, 'Expect dc.negotiated to be false'); |
| 518 | }, 'createDataChannel with negotiated false should succeed'); |
| 519 | |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 520 | test(t => { |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 521 | const pc = new RTCPeerConnection; |
| 522 | t.add_cleanup(() => pc.close()); |
| 523 | |
| 524 | const dc = pc.createDataChannel('', { |
| 525 | negotiated: false, |
| 526 | id: 42 |
| 527 | }); |
| 528 | assert_equals(dc.negotiated, false, 'Expect dc.negotiated to be false'); |
| 529 | assert_equals(dc.id, null, 'Expect dc.id to be ignored (null)'); |
| 530 | }, 'createDataChannel with negotiated false and id 42 should ignore the id'); |
| 531 | |
| 532 | /* |
| 533 | 6.2. createDataChannel |
| 534 | 16. If [[Negotiated]] is true and [[DataChannelId]] is null, throw a TypeError. |
| 535 | */ |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 536 | test(t => { |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 537 | const pc = new RTCPeerConnection(); |
| 538 | t.add_cleanup(() => pc.close()); |
| 539 | |
| Boris Zbarsky | 71bf020 | 2020-01-21 04:26:37 | [diff] [blame^] | 540 | assert_throws_js(TypeError, () => |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 541 | pc.createDataChannel('test', { |
| 542 | negotiated: true |
| 543 | })); |
| 544 | }, 'createDataChannel with negotiated true and id not defined should throw TypeError'); |
| 545 | |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 546 | /* |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 547 | 4.4.1.6. Set the RTCSessionSessionDescription |
| 548 | 2.2.6. If description is of type "answer" or "pranswer", then run the |
| 549 | following steps: |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 550 | 3. If description negotiates the DTLS role of the SCTP transport, and there is an |
| 551 | RTCDataChannel with a null id, then generate an ID according to |
| 552 | [RTCWEB-DATA-PROTOCOL]. [...] |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 553 | |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 554 | 6.1. createDataChannel |
| 555 | 21. If the [[DataChannelId]] slot is null (due to no ID being passed into |
| 556 | createDataChannel, or [[Negotiated]] being false), and the DTLS role of the SCTP |
| 557 | transport has already been negotiated, then initialize [[DataChannelId]] to a value |
| 558 | generated by the user agent, according to [RTCWEB-DATA-PROTOCOL], and skip |
| 559 | to the next step. If no available ID could be generated, or if the value of the |
| 560 | [[DataChannelId]] slot is being used by an existing RTCDataChannel, throw an |
| 561 | OperationError exception. |
| 562 | |
| 563 | Note |
| 564 | If the [[DataChannelId]] slot is null after this step, it will be populated once |
| 565 | the DTLS role is determined during the process of setting an RTCSessionDescription. |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 566 | */ |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 567 | promise_test(async t => { |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 568 | const pc1 = new RTCPeerConnection(); |
| 569 | const pc2 = new RTCPeerConnection(); |
| 570 | t.add_cleanup(() => pc1.close()); |
| 571 | t.add_cleanup(() => pc2.close()); |
| 572 | |
| 573 | const negotiatedDc = pc1.createDataChannel('negotiated-channel', { |
| 574 | negotiated: true, |
| 575 | id: 42, |
| 576 | }); |
| 577 | assert_equals(negotiatedDc.id, 42, 'Expect negotiatedDc.id to be 42'); |
| 578 | |
| 579 | const dc1 = pc1.createDataChannel('channel'); |
| 580 | assert_equals(dc1.id, null, 'Expect initial id to be null'); |
| 581 | |
| 582 | const offer = await pc1.createOffer(); |
| 583 | await Promise.all([pc1.setLocalDescription(offer), pc2.setRemoteDescription(offer)]); |
| 584 | const answer = await pc2.createAnswer(); |
| 585 | await pc1.setRemoteDescription(answer); |
| 586 | |
| 587 | assert_not_equals(dc1.id, null, |
| 588 | 'Expect dc1.id to be assigned after remote description has been set'); |
| 589 | |
| 590 | assert_greater_than_equal(dc1.id, 0, |
| 591 | 'Expect dc1.id to be set to valid unsigned short'); |
| 592 | |
| 593 | assert_less_than(dc1.id, 65535, |
| 594 | 'Expect dc1.id to be set to valid unsigned short'); |
| 595 | |
| 596 | const dc2 = pc1.createDataChannel('channel'); |
| 597 | |
| 598 | assert_not_equals(dc2.id, null, |
| 599 | 'Expect dc2.id to be assigned after remote description has been set'); |
| 600 | |
| 601 | assert_greater_than_equal(dc2.id, 0, |
| 602 | 'Expect dc2.id to be set to valid unsigned short'); |
| 603 | |
| 604 | assert_less_than(dc2.id, 65535, |
| 605 | 'Expect dc2.id to be set to valid unsigned short'); |
| 606 | |
| 607 | assert_not_equals(dc2, dc1, |
| 608 | 'Expect channels created from same label to be different'); |
| 609 | |
| 610 | assert_equals(dc2.label, dc1.label, |
| 611 | 'Expect different channels can have the same label but different id'); |
| 612 | |
| 613 | assert_not_equals(dc2.id, dc1.id, |
| 614 | 'Expect different channels can have the same label but different id'); |
| 615 | |
| 616 | assert_equals(negotiatedDc.id, 42, |
| 617 | 'Expect negotiatedDc.id to be 42 after remote description has been set'); |
| 618 | }, 'Channels created (after setRemoteDescription) should have id assigned'); |
| 619 | |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 620 | test(t => { |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 621 | const pc = new RTCPeerConnection(); |
| Philipp Hancke | 1622a02 | 2018-06-11 10:00:53 | [diff] [blame] | 622 | t.add_cleanup(() => pc.close()); |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 623 | |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 624 | const dc1 = pc.createDataChannel('channel-1', { |
| 625 | negotiated: true, |
| 626 | id: 42, |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 627 | }); |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 628 | assert_equals(dc1.id, 42, |
| 629 | 'Expect dc1.id to be 42'); |
| 630 | |
| 631 | const dc2 = pc.createDataChannel('channel-2', { |
| 632 | negotiated: true, |
| 633 | id: 43, |
| 634 | }); |
| 635 | assert_equals(dc2.id, 43, |
| 636 | 'Expect dc2.id to be 43'); |
| 637 | |
| 638 | assert_throws('OperationError', () => |
| 639 | pc.createDataChannel('channel-3', { |
| 640 | negotiated: true, |
| 641 | id: 42, |
| 642 | })); |
| 643 | |
| 644 | }, 'Reusing a data channel id that is in use should throw OperationError'); |
| 645 | |
| 646 | // We've seen implementations behaving differently before and after the connection has been |
| 647 | // established. |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 648 | promise_test(async t => { |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 649 | const pc1 = new RTCPeerConnection(); |
| 650 | const pc2 = new RTCPeerConnection(); |
| 651 | t.add_cleanup(() => pc1.close()); |
| 652 | t.add_cleanup(() => pc2.close()); |
| 653 | |
| 654 | const dc1 = pc1.createDataChannel('channel-1', { |
| 655 | negotiated: true, |
| 656 | id: 42, |
| 657 | }); |
| 658 | assert_equals(dc1.id, 42, 'Expect dc1.id to be 42'); |
| 659 | |
| 660 | const dc2 = pc1.createDataChannel('channel-2', { |
| 661 | negotiated: true, |
| 662 | id: 43, |
| 663 | }); |
| 664 | assert_equals(dc2.id, 43, 'Expect dc2.id to be 43'); |
| 665 | |
| 666 | const offer = await pc1.createOffer(); |
| 667 | await Promise.all([pc1.setLocalDescription(offer), pc2.setRemoteDescription(offer)]); |
| 668 | const answer = await pc2.createAnswer(); |
| 669 | await pc1.setRemoteDescription(answer); |
| 670 | |
| 671 | assert_equals(dc1.id, 42, 'Expect dc1.id to be 42'); |
| 672 | |
| 673 | assert_equals(dc2.id, 43, 'Expect dc2.id to be 43'); |
| 674 | |
| 675 | assert_throws('OperationError', () => |
| 676 | pc1.createDataChannel('channel-3', { |
| 677 | negotiated: true, |
| 678 | id: 42, |
| 679 | })); |
| 680 | }, 'Reusing a data channel id that is in use (after setRemoteDescription) should throw ' + |
| 681 | 'OperationError'); |
| 682 | |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 683 | promise_test(async t => { |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 684 | const pc1 = new RTCPeerConnection(); |
| 685 | const pc2 = new RTCPeerConnection(); |
| 686 | t.add_cleanup(() => pc1.close()); |
| 687 | t.add_cleanup(() => pc2.close()); |
| 688 | |
| 689 | const dc1 = pc1.createDataChannel('channel-1'); |
| 690 | |
| 691 | const offer = await pc1.createOffer(); |
| 692 | await Promise.all([pc1.setLocalDescription(offer), pc2.setRemoteDescription(offer)]); |
| 693 | const answer = await pc2.createAnswer(); |
| 694 | await pc1.setRemoteDescription(answer); |
| 695 | |
| 696 | assert_not_equals(dc1.id, null, |
| 697 | 'Expect dc1.id to be assigned after remote description has been set'); |
| 698 | |
| 699 | assert_throws('OperationError', () => |
| 700 | pc1.createDataChannel('channel-2', { |
| 701 | negotiated: true, |
| 702 | id: dc1.id, |
| 703 | })); |
| 704 | }, 'Reusing a data channel id that is in use (after setRemoteDescription, negotiated via DCEP) ' + |
| 705 | 'should throw OperationError'); |
| 706 | |
| 707 | // Based on https://bugzilla.mozilla.org/show_bug.cgi?id=1441723 |
| Harald Alvestrand | f6cf3bd | 2019-04-04 12:39:57 | [diff] [blame] | 708 | promise_test(async t => { |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 709 | const pc1 = new RTCPeerConnection(); |
| 710 | const pc2 = new RTCPeerConnection(); |
| 711 | t.add_cleanup(() => pc1.close()); |
| 712 | t.add_cleanup(() => pc2.close()); |
| 713 | |
| 714 | await createDataChannelPair(pc1, pc2); |
| 715 | |
| 716 | const dc = pc1.createDataChannel(''); |
| 717 | assert_equals(dc.readyState, 'connecting', 'Channel should be in the connecting state'); |
| 718 | }, 'New data channel should be in the connecting state after creation (after connection ' + |
| 719 | 'establishment)'); |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 720 | |
| moz-wptsync-bot | b0d2aa5 | 2019-10-02 14:44:10 | [diff] [blame] | 721 | promise_test(async t => { |
| 722 | const pc1 = new RTCPeerConnection(); |
| 723 | const pc2 = new RTCPeerConnection(); |
| 724 | t.add_cleanup(() => pc1.close()); |
| 725 | t.add_cleanup(() => pc2.close()); |
| 726 | const stream = await getNoiseStream({audio: true, video: true}); |
| 727 | t.add_cleanup(() => stopTracks(stream)); |
| 728 | const audio = stream.getAudioTracks()[0]; |
| 729 | const video = stream.getVideoTracks()[0]; |
| 730 | pc1.addTrack(audio, stream); |
| 731 | pc1.addTrack(video, stream); |
| 732 | await createDataChannelPair(pc1, pc2); |
| 733 | }, 'addTrack, then createDataChannel, should negotiate properly'); |
| 734 | |
| 735 | promise_test(async t => { |
| 736 | const pc1 = new RTCPeerConnection({bundlePolicy: "max-bundle"}); |
| 737 | const pc2 = new RTCPeerConnection(); |
| 738 | t.add_cleanup(() => pc1.close()); |
| 739 | t.add_cleanup(() => pc2.close()); |
| 740 | const stream = await getNoiseStream({audio: true, video: true}); |
| 741 | t.add_cleanup(() => stopTracks(stream)); |
| 742 | const audio = stream.getAudioTracks()[0]; |
| 743 | const video = stream.getVideoTracks()[0]; |
| 744 | pc1.addTrack(audio, stream); |
| 745 | pc1.addTrack(video, stream); |
| 746 | await createDataChannelPair(pc1, pc2); |
| 747 | }, 'addTrack, then createDataChannel, should negotiate properly when max-bundle is used'); |
| 748 | |
| 749 | promise_test(async t => { |
| 750 | const pc1 = new RTCPeerConnection({bundlePolicy: "max-bundle"}); |
| 751 | const pc2 = new RTCPeerConnection(); |
| 752 | t.add_cleanup(() => pc1.close()); |
| 753 | t.add_cleanup(() => pc2.close()); |
| 754 | const stream = await getNoiseStream({audio: true, video: true}); |
| 755 | t.add_cleanup(() => stopTracks(stream)); |
| 756 | const audio = stream.getAudioTracks()[0]; |
| 757 | const video = stream.getVideoTracks()[0]; |
| 758 | pc1.addTrack(audio, stream); |
| 759 | pc1.addTrack(video, stream); |
| 760 | const [dc1, dc2] = await createDataChannelPair(pc1, pc2); |
| 761 | |
| 762 | pc2.getTransceivers()[0].stop(); |
| 763 | const dc1Closed = new Promise(r => dc1.onclose = r); |
| 764 | await doSignalingHandshake(pc1, pc2); |
| 765 | await dc1Closed; |
| 766 | }, 'Stopping the bundle-tag when there is a DataChannel in the bundle should kill the DataChannel'); |
| 767 | |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 768 | /* |
| Soares Chen | 6a9b781 | 2017-06-08 13:25:47 | [diff] [blame] | 769 | Untestable |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 770 | 6.1. createDataChannel |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 771 | 19. If a setting, either [[MaxPacketLifeTime]] or [[MaxRetransmits]], has been set to |
| 772 | indicate unreliable mode, and that value exceeds the maximum value supported |
| 773 | by the user agent, the value MUST be set to the user agents maximum value. |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 774 | |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 775 | 23. Return channel and continue the following steps in parallel. |
| 776 | 24. Create channel's associated underlying data transport and configure |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 777 | it according to the relevant properties of channel. |
| 778 | |
| 779 | Tested in RTCPeerConnection-onnegotiationneeded.html |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 780 | 22. If channel is the first RTCDataChannel created on connection, update the |
| 781 | negotiation-needed flag for connection. |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 782 | |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 783 | Tested in RTCDataChannel-id.html |
| 784 | - Odd/even rules for '.id' |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 785 | |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 786 | Tested in RTCDataChannel-dcep.html |
| 787 | - Transmission of '.label' and further options |
| Soares Chen | 199b65f | 2017-06-21 09:20:39 | [diff] [blame] | 788 | */ |
| foolip | 72525e9 | 2017-04-24 09:31:03 | [diff] [blame] | 789 | </script> |