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