blob: 5e321681336ece75202d9c469227d60da4ceee39 [file] [log] [blame]
Soares Chenb20cf6b2017-05-19 12:28:431<!doctype html>
2<title>Test RTCPeerConnection.prototype.addIceCandidate</title>
3<script src="/resources/testharness.js"></script>
4<script src="/resources/testharnessreport.js"></script>
5<script>
6 'use strict';
7
Soares Chen25c23902017-06-27 15:32:578 // Test is based on the following editor draft:
9 // https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.htm
10
Soares Chen25c23902017-06-27 15:32:5711 /*
12 4.3.2. Interface Definition
13 interface RTCPeerConnection : EventTarget {
14 ...
15 Promise<void> addIceCandidate((RTCIceCandidateInit or RTCIceCandidate) candidate);
16 };
17
18 interface RTCIceCandidate {
19 readonly attribute DOMString candidate;
20 readonly attribute DOMString? sdpMid;
21 readonly attribute unsigned short? sdpMLineIndex;
22 readonly attribute DOMString? ufrag;
23 ...
24 };
25
26 dictionary RTCIceCandidateInit {
27 DOMString candidate = "";
28 DOMString? sdpMid = null;
29 unsigned short? sdpMLineIndex = null;
30 DOMString ufrag;
31 };
32 */
33
Soares Chenb20cf6b2017-05-19 12:28:4334 // SDP copied from JSEP Example 7.1
35 // It contains two media streams with different ufrags
36 // to test if candidate is added to the correct stream
37 const sdp = `v=0
38o=- 4962303333179871722 1 IN IP4 0.0.0.0
39s=-
40t=0 0
41a=ice-options:trickle
42a=group:BUNDLE a1 v1
43a=group:LS a1 v1
44m=audio 10100 UDP/TLS/RTP/SAVPF 96 0 8 97 98
45c=IN IP4 203.0.113.100
46a=mid:a1
47a=sendrecv
48a=rtpmap:96 opus/48000/2
49a=rtpmap:0 PCMU/8000
50a=rtpmap:8 PCMA/8000
51a=rtpmap:97 telephone-event/8000
52a=rtpmap:98 telephone-event/48000
53a=maxptime:120
54a=extmap:1 urn:ietf:params:rtp-hdrext:sdes:mid
55a=extmap:2 urn:ietf:params:rtp-hdrext:ssrc-audio-level
56a=msid:47017fee-b6c1-4162-929c-a25110252400 f83006c5-a0ff-4e0a-9ed9-d3e6747be7d9
57a=ice-ufrag:ETEn
58a=ice-pwd:OtSK0WpNtpUjkY4+86js7ZQl
59a=fingerprint:sha-256 19:E2:1C:3B:4B:9F:81:E6:B8:5C:F4:A5:A8:D8:73:04:BB:05:2F:70:9F:04:A9:0E:05:E9:26:33:E8:70:88:A2
60a=setup:actpass
61a=dtls-id:1
62a=rtcp:10101 IN IP4 203.0.113.100
63a=rtcp-mux
64a=rtcp-rsize
65m=video 10102 UDP/TLS/RTP/SAVPF 100 101
66c=IN IP4 203.0.113.100
67a=mid:v1
68a=sendrecv
69a=rtpmap:100 VP8/90000
70a=rtpmap:101 rtx/90000
71a=fmtp:101 apt=100
72a=extmap:1 urn:ietf:params:rtp-hdrext:sdes:mid
73a=rtcp-fb:100 ccm fir
74a=rtcp-fb:100 nack
75a=rtcp-fb:100 nack pli
76a=msid:47017fee-b6c1-4162-929c-a25110252400 f30bdb4a-5db8-49b5-bcdc-e0c9a23172e0
77a=ice-ufrag:BGKk
78a=ice-pwd:mqyWsAjvtKwTGnvhPztQ9mIf
79a=fingerprint:sha-256 19:E2:1C:3B:4B:9F:81:E6:B8:5C:F4:A5:A8:D8:73:04:BB:05:2F:70:9F:04:A9:0E:05:E9:26:33:E8:70:88:A2
80a=setup:actpass
81a=dtls-id:1
82a=rtcp:10103 IN IP4 203.0.113.100
83a=rtcp-mux
84a=rtcp-rsize
85`;
86
87 const sessionDesc = { type: 'offer', sdp };
88
89 // valid candidate attributes
90 const sdpMid = 'a1';
91 const sdpMLineIndex = 0;
92 const ufrag = 'ETEn';
93
94 const sdpMid2 = 'v1';
95 const sdpMLineIndex2 = 1;
96 const ufrag2 = 'BGKk';
97
98 const mediaLine1 = 'm=audio';
99 const mediaLine2 = 'm=video';
100
101 const candidateStr1 = 'candidate:1 1 udp 2113929471 203.0.113.100 10100 typ host';
102 const candidateStr2 = 'candidate:1 2 udp 2113929470 203.0.113.100 10101 typ host';
103 const invalidCandidateStr = '(Invalid) candidate \r\n string';
104
105 const candidateLine1 = `a=${candidateStr1}`;
106 const candidateLine2 = `a=${candidateStr2}`;
107 const endOfCandidateLine = 'a=end-of-candidates';
108
109 // Copied from MDN
110 function escapeRegExp(string) {
111 return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
112 }
113
114 // Check that a candidate line is found after the first media line
115 // but before the second, i.e. it belongs to the first media stream
116 function assert_candidate_line_between(sdp, beforeMediaLine, candidateLine, afterMediaLine) {
117 const line1 = escapeRegExp(beforeMediaLine);
118 const line2 = escapeRegExp(candidateLine);
119 const line3 = escapeRegExp(afterMediaLine);
120
121 const regex = new RegExp(`${line1}[^]+${line2}[^]+${line3}`);
122
123 assert_true(regex.test(sdp),
124 `Expect candidate line to be found between media lines ${beforeMediaLine} and ${afterMediaLine}`);
125 }
126
127 // Check that a candidate line is found after the second media line
128 // i.e. it belongs to the second media stream
129 function assert_candidate_line_after(sdp, beforeMediaLine, candidateLine) {
130 const line1 = escapeRegExp(beforeMediaLine);
131 const line2 = escapeRegExp(candidateLine);
132
133 const regex = new RegExp(`${line1}[^]+${line2}`);
134
135 assert_true(regex.test(sdp),
136 `Expect candidate line to be found after media line ${beforeMediaLine}`);
137 }
138
Soares Chen25c23902017-06-27 15:32:57139 // Reject because WebIDL for addIceCandidate does not allow null argument
140 // null can be accidentally passed from onicecandidate event handler
141 // when null is used to indicate end of candidate
142 promise_test(t => {
143 const pc = new RTCPeerConnection();
144
Philipp Hancke1622a022018-06-11 10:00:53145 t.add_cleanup(() => pc.close());
146
Soares Chen25c23902017-06-27 15:32:57147 return pc.setRemoteDescription(sessionDesc)
148 .then(() =>
149 promise_rejects(t, new TypeError(),
150 pc.addIceCandidate(null)));
151 }, 'Add null candidate should reject with TypeError');
152
153 /*
154 4.3.2. addIceCandidate
155 4. Return the result of enqueuing the following steps:
156 1. If remoteDescription is null return a promise rejected with a
157 newly created InvalidStateError.
158 */
Soares Chenb20cf6b2017-05-19 12:28:43159 promise_test(t => {
160 const pc = new RTCPeerConnection();
161
Philipp Hancke1622a022018-06-11 10:00:53162 t.add_cleanup(() => pc.close());
163
Soares Chenb20cf6b2017-05-19 12:28:43164 return promise_rejects(t, 'InvalidStateError',
165 pc.addIceCandidate({
166 candidate: candidateStr1,
167 sdpMid, sdpMLineIndex, ufrag
168 }));
169 }, 'Add ICE candidate before setting remote description should reject with InvalidStateError');
170
Soares Chen25c23902017-06-27 15:32:57171 /*
Soares Chen25c23902017-06-27 15:32:57172 Success cases
173 */
Soares Chenb20cf6b2017-05-19 12:28:43174 promise_test(t => {
175 const pc = new RTCPeerConnection();
176
Philipp Hancke1622a022018-06-11 10:00:53177 t.add_cleanup(() => pc.close());
178
Soares Chenb20cf6b2017-05-19 12:28:43179 return pc.setRemoteDescription(sessionDesc)
180 .then(() => pc.addIceCandidate({
181 candidate: candidateStr1,
182 sdpMid, sdpMLineIndex, ufrag
183 }));
184 }, 'Add ICE candidate after setting remote description should succeed');
185
186 promise_test(t => {
187 const pc = new RTCPeerConnection();
188
Philipp Hancke1622a022018-06-11 10:00:53189 t.add_cleanup(() => pc.close());
190
Soares Chenb20cf6b2017-05-19 12:28:43191 return pc.setRemoteDescription(sessionDesc)
192 .then(() => pc.addIceCandidate(new RTCIceCandidate({
193 candidate: candidateStr1,
194 sdpMid, sdpMLineIndex, ufrag
195 })));
196 }, 'Add ICE candidate with RTCIceCandidate should succeed');
197
198 promise_test(t => {
199 const pc = new RTCPeerConnection();
200
Philipp Hancke1622a022018-06-11 10:00:53201 t.add_cleanup(() => pc.close());
202
Soares Chenb20cf6b2017-05-19 12:28:43203 return pc.setRemoteDescription(sessionDesc)
Soares Chen25c23902017-06-27 15:32:57204 .then(() => pc.addIceCandidate({ sdpMid }));
205 }, 'Add candidate with only valid sdpMid should succeed');
206
207 promise_test(t => {
208 const pc = new RTCPeerConnection();
209
Philipp Hancke1622a022018-06-11 10:00:53210 t.add_cleanup(() => pc.close());
211
Soares Chen25c23902017-06-27 15:32:57212 return pc.setRemoteDescription(sessionDesc)
213 .then(() => pc.addIceCandidate({ sdpMLineIndex }));
214 }, 'Add candidate with only valid sdpMLineIndex should succeed');
215
216 /*
217 4.3.2. addIceCandidate
218 4.6.2. If candidate is applied successfully, the user agent MUST queue
219 a task that runs the following steps:
220 2. Let remoteDescription be connection's pendingRemoteDescription
221 if not null, otherwise connection's currentRemoteDescription.
222 3. Add candidate to remoteDescription.
223 */
224 promise_test(t => {
225 const pc = new RTCPeerConnection();
226
Philipp Hancke1622a022018-06-11 10:00:53227 t.add_cleanup(() => pc.close());
228
Soares Chen25c23902017-06-27 15:32:57229 return pc.setRemoteDescription(sessionDesc)
Soares Chenb20cf6b2017-05-19 12:28:43230 .then(() => pc.addIceCandidate({
231 candidate: candidateStr1,
232 sdpMid, sdpMLineIndex, ufrag
233 }))
234 .then(() => {
235 assert_candidate_line_between(pc.remoteDescription.sdp,
236 mediaLine1, candidateLine1, mediaLine2);
237 });
Soares Chen25c23902017-06-27 15:32:57238 }, 'addIceCandidate with first sdpMid and sdpMLineIndex add candidate to first media stream');
Soares Chenb20cf6b2017-05-19 12:28:43239
240 promise_test(t => {
241 const pc = new RTCPeerConnection();
242
Philipp Hancke1622a022018-06-11 10:00:53243 t.add_cleanup(() => pc.close());
244
Soares Chenb20cf6b2017-05-19 12:28:43245 return pc.setRemoteDescription(sessionDesc)
246 .then(() => pc.addIceCandidate({
247 candidate: candidateStr2,
248 sdpMid: sdpMid2,
249 sdpMLineIndex: sdpMLineIndex2,
250 ufrag: ufrag2
251 }))
252 .then(() => {
253 assert_candidate_line_after(pc.remoteDescription.sdp,
254 mediaLine2, candidateLine2);
255 });
Soares Chen25c23902017-06-27 15:32:57256 }, 'addIceCandidate with second sdpMid and sdpMLineIndex should add candidate to second media stream');
257
258 promise_test(t => {
259 const pc = new RTCPeerConnection();
260
Philipp Hancke1622a022018-06-11 10:00:53261 t.add_cleanup(() => pc.close());
262
Soares Chen25c23902017-06-27 15:32:57263 return pc.setRemoteDescription(sessionDesc)
264 .then(() => pc.addIceCandidate({
265 candidate: candidateStr1,
266 sdpMid, sdpMLineIndex,
267 ufrag: null
268 }))
269 .then(() => {
270 assert_candidate_line_between(pc.remoteDescription.sdp,
271 mediaLine1, candidateLine1, mediaLine2);
272 });
273 }, 'Add candidate for first media stream with null ufrag should add candidate to first media stream');
Soares Chenb20cf6b2017-05-19 12:28:43274
275 promise_test(t => {
276 const pc = new RTCPeerConnection();
277
Philipp Hancke1622a022018-06-11 10:00:53278 t.add_cleanup(() => pc.close());
279
Soares Chenb20cf6b2017-05-19 12:28:43280 return pc.setRemoteDescription(sessionDesc)
281 .then(() => pc.addIceCandidate({
282 candidate: candidateStr1,
283 sdpMid, sdpMLineIndex, ufrag
284 }))
285 .then(() => pc.addIceCandidate({
286 candidate: candidateStr2,
287 sdpMid: sdpMid2,
288 sdpMLineIndex: sdpMLineIndex2,
289 ufrag: ufrag2
290 }))
291 .then(() => {
292 assert_candidate_line_between(pc.remoteDescription.sdp,
293 mediaLine1, candidateLine1, mediaLine2);
294
295 assert_candidate_line_after(pc.remoteDescription.sdp,
296 mediaLine2, candidateLine2);
297 });
Soares Chen25c23902017-06-27 15:32:57298 }, 'Adding multiple candidates should add candidates to their corresponding media stream');
Soares Chenb20cf6b2017-05-19 12:28:43299
Soares Chen25c23902017-06-27 15:32:57300 /*
301 4.3.2. addIceCandidate
302 4.6. If candidate.candidate is an empty string, process candidate as an
303 end-of-candidates indication for the corresponding media description
304 and ICE candidate generation.
305 2. If candidate is applied successfully, the user agent MUST queue
306 a task that runs the following steps:
307 2. Let remoteDescription be connection's pendingRemoteDescription
308 if not null, otherwise connection's currentRemoteDescription.
309 3. Add candidate to remoteDescription.
310 */
Soares Chenb20cf6b2017-05-19 12:28:43311 promise_test(t => {
312 const pc = new RTCPeerConnection();
313
Philipp Hancke1622a022018-06-11 10:00:53314 t.add_cleanup(() => pc.close());
315
Soares Chenb20cf6b2017-05-19 12:28:43316 return pc.setRemoteDescription(sessionDesc)
317 .then(() => pc.addIceCandidate({
318 candidate: candidateStr1,
319 sdpMid, sdpMLineIndex, ufrag
320 }))
321 .then(() => pc.addIceCandidate({
322 candidate: '',
323 sdpMid, sdpMLineIndex,
324 ufrag
325 }))
326 .then(() => {
327 assert_candidate_line_between(pc.remoteDescription.sdp,
328 mediaLine1, candidateLine1, mediaLine2);
329
330 assert_candidate_line_between(pc.remoteDescription.sdp,
331 mediaLine1, endOfCandidateLine, mediaLine2);
332 });
333 }, 'Add with empty candidate string (end of candidate) should succeed');
334
Soares Chen25c23902017-06-27 15:32:57335 /*
336 4.3.2. addIceCandidate
337 3. If both sdpMid and sdpMLineIndex are null, return a promise rejected
338 with a newly created TypeError.
339 */
Soares Chenb20cf6b2017-05-19 12:28:43340 promise_test(t => {
341 const pc = new RTCPeerConnection();
342
Philipp Hancke1622a022018-06-11 10:00:53343 t.add_cleanup(() => pc.close());
344
Soares Chenb20cf6b2017-05-19 12:28:43345 return pc.setRemoteDescription(sessionDesc)
346 .then(() =>
347 promise_rejects(t, new TypeError(),
348 pc.addIceCandidate({
349 candidate: candidateStr1,
350 sdpMid: null,
351 sdpMLineIndex: null
352 })));
353 }, 'Add candidate with both sdpMid and sdpMLineIndex manually set to null should reject with TypeError');
354
355 promise_test(t => {
356 const pc = new RTCPeerConnection();
357
Philipp Hancke1622a022018-06-11 10:00:53358 t.add_cleanup(() => pc.close());
359
Soares Chenb20cf6b2017-05-19 12:28:43360 return pc.setRemoteDescription(sessionDesc)
361 .then(() =>
Soares Chen25c23902017-06-27 15:32:57362 promise_rejects(t, new TypeError(),
Soares Chenb20cf6b2017-05-19 12:28:43363 pc.addIceCandidate({
Soares Chen25c23902017-06-27 15:32:57364 candidate: candidateStr1
Soares Chenb20cf6b2017-05-19 12:28:43365 })));
Soares Chen25c23902017-06-27 15:32:57366 }, 'Add candidate with only valid candidate string should reject with TypeError');
Soares Chenb20cf6b2017-05-19 12:28:43367
368 promise_test(t => {
369 const pc = new RTCPeerConnection();
370
Philipp Hancke1622a022018-06-11 10:00:53371 t.add_cleanup(() => pc.close());
372
Soares Chenb20cf6b2017-05-19 12:28:43373 return pc.setRemoteDescription(sessionDesc)
374 .then(() =>
375 promise_rejects(t, new TypeError(),
376 pc.addIceCandidate({
377 candidate: invalidCandidateStr,
378 sdpMid: null,
379 sdpMLineIndex: null
380 })));
381 }, 'Add candidate with invalid candidate string and both sdpMid and sdpMLineIndex null should reject with TypeError');
382
383 promise_test(t => {
384 const pc = new RTCPeerConnection();
385
Philipp Hancke1622a022018-06-11 10:00:53386 t.add_cleanup(() => pc.close());
387
Soares Chenb20cf6b2017-05-19 12:28:43388 return pc.setRemoteDescription(sessionDesc)
389 .then(() =>
390 promise_rejects(t, new TypeError(),
Soares Chenb20cf6b2017-05-19 12:28:43391 pc.addIceCandidate({})));
392 }, 'Add candidate with empty dict should reject with TypeError');
393
394 promise_test(t => {
395 const pc = new RTCPeerConnection();
396
Philipp Hancke1622a022018-06-11 10:00:53397 t.add_cleanup(() => pc.close());
398
Soares Chenb20cf6b2017-05-19 12:28:43399 return pc.setRemoteDescription(sessionDesc)
400 .then(() =>
401 promise_rejects(t, new TypeError(),
402 pc.addIceCandidate({
403 candidate: '',
404 sdpMid: null,
405 sdpMLineIndex: null,
406 ufrag: undefined
407 })));
408 }, 'Add candidate with manually filled default values should reject with TypeError');
409
Soares Chen25c23902017-06-27 15:32:57410 /*
411 4.3.2. addIceCandidate
412 4.3. If candidate.sdpMid is not null, run the following steps:
413 1. If candidate.sdpMid is not equal to the mid of any media
414 description in remoteDescription , reject p with a newly
415 created OperationError and abort these steps.
416 */
Soares Chenb20cf6b2017-05-19 12:28:43417 promise_test(t => {
418 const pc = new RTCPeerConnection();
419
Philipp Hancke1622a022018-06-11 10:00:53420 t.add_cleanup(() => pc.close());
421
Soares Chenb20cf6b2017-05-19 12:28:43422 return pc.setRemoteDescription(sessionDesc)
423 .then(() =>
424 promise_rejects(t, 'OperationError',
425 pc.addIceCandidate({
426 candidate: candidateStr1,
427 sdpMid: 'invalid', sdpMLineIndex, ufrag
428 })));
429 }, 'Add candidate with invalid sdpMid should reject with OperationError');
430
Soares Chen25c23902017-06-27 15:32:57431 /*
432 4.3.2. addIceCandidate
433 4.4. Else, if candidate.sdpMLineIndex is not null, run the following
434 steps:
435 1. If candidate.sdpMLineIndex is equal to or larger than the
436 number of media descriptions in remoteDescription , reject p
437 with a newly created OperationError and abort these steps.
438 */
Soares Chenb20cf6b2017-05-19 12:28:43439 promise_test(t => {
440 const pc = new RTCPeerConnection();
441
Philipp Hancke1622a022018-06-11 10:00:53442 t.add_cleanup(() => pc.close());
443
Soares Chenb20cf6b2017-05-19 12:28:43444 return pc.setRemoteDescription(sessionDesc)
445 .then(() =>
446 promise_rejects(t, 'OperationError',
447 pc.addIceCandidate({
448 candidate: candidateStr1,
449 sdpMLineIndex: 2,
450 ufrag
451 })));
452 }, 'Add candidate with invalid sdpMLineIndex should reject with OperationError');
453
454 // There is an "Else" for the statement:
455 // "Else, if candidate.sdpMLineIndex is not null, ..."
456 promise_test(t => {
457 const pc = new RTCPeerConnection();
458
Philipp Hancke1622a022018-06-11 10:00:53459 t.add_cleanup(() => pc.close());
460
Soares Chenb20cf6b2017-05-19 12:28:43461 return pc.setRemoteDescription(sessionDesc)
462 .then(() => pc.addIceCandidate({
463 candidate: candidateStr1,
464 sdpMid,
465 sdpMLineIndex: 2,
466 ufrag
467 }));
468 }, 'Invalid sdpMLineIndex should be ignored if valid sdpMid is provided');
469
470 promise_test(t => {
471 const pc = new RTCPeerConnection();
472
Philipp Hancke1622a022018-06-11 10:00:53473 t.add_cleanup(() => pc.close());
474
Soares Chenb20cf6b2017-05-19 12:28:43475 return pc.setRemoteDescription(sessionDesc)
Soares Chenb20cf6b2017-05-19 12:28:43476 .then(() => pc.addIceCandidate({
477 candidate: candidateStr2,
478 sdpMid: sdpMid2,
479 sdpMLineIndex: sdpMLineIndex2,
480 ufrag: null
481 }))
482 .then(() => {
483 assert_candidate_line_after(pc.remoteDescription.sdp,
484 mediaLine2, candidateLine2);
485 });
486 }, 'Add candidate for media stream 2 with null ufrag should succeed');
487
Soares Chen25c23902017-06-27 15:32:57488 /*
489 4.3.2. addIceCandidate
490 4.5. If candidate.ufrag is neither undefined nor null, and is not equal
491 to any ufrag present in the corresponding media description of an
492 applied remote description, reject p with a newly created
493 OperationError and abort these steps.
494 */
495 promise_test(t => {
496 const pc = new RTCPeerConnection();
497
Philipp Hancke1622a022018-06-11 10:00:53498 t.add_cleanup(() => pc.close());
499
Soares Chen25c23902017-06-27 15:32:57500 return pc.setRemoteDescription(sessionDesc)
501 .then(() =>
502 promise_rejects(t, 'OperationError',
503 pc.addIceCandidate({
504 candidate: candidateStr1,
505 sdpMid, sdpMLineIndex,
506 ufrag: 'invalid'
507 })));
508 }, 'Add candidate with invalid ufrag should reject with OperationError');
509
510 /*
511 4.3.2. addIceCandidate
512 4.6.1. If candidate could not be successfully added the user agent MUST
513 queue a task that runs the following steps:
514 2. Reject p with a DOMException object whose name attribute has
515 the value OperationError and abort these steps.
516 */
517 promise_test(t => {
518 const pc = new RTCPeerConnection();
519
Philipp Hancke1622a022018-06-11 10:00:53520 t.add_cleanup(() => pc.close());
521
Soares Chen25c23902017-06-27 15:32:57522 return pc.setRemoteDescription(sessionDesc)
523 .then(() =>
524 promise_rejects(t, 'OperationError',
525 pc.addIceCandidate({
526 candidate: invalidCandidateStr,
527 sdpMid, sdpMLineIndex, ufrag
528 })));
529 }, 'Add candidate with invalid candidate string should reject with OperationError');
530
Soares Chenb20cf6b2017-05-19 12:28:43531 promise_test(t => {
532 const pc = new RTCPeerConnection();
533
Philipp Hancke1622a022018-06-11 10:00:53534 t.add_cleanup(() => pc.close());
535
Soares Chenb20cf6b2017-05-19 12:28:43536 return pc.setRemoteDescription(sessionDesc)
537 .then(() =>
538 promise_rejects(t, 'OperationError',
539 pc.addIceCandidate({
540 candidate: candidateStr2,
541 sdpMid: sdpMid2,
542 sdpMLineIndex: sdpMLineIndex2,
543 ufrag: ufrag
544 })));
545 }, 'Add candidate with sdpMid belonging to different ufrag should reject with OperationError');
546
Soares Chen25c23902017-06-27 15:32:57547 /*
548 TODO
549 4.3.2. addIceCandidate
550 4.6. In parallel, add the ICE candidate candidate as described in [JSEP]
551 (section 4.1.17.). Use candidate.ufrag to identify the ICE generation;
Soares Chenb20cf6b2017-05-19 12:28:43552
Soares Chen25c23902017-06-27 15:32:57553 If the ufrag is null, process the candidate for the most recent ICE
554 generation.
555
556 - Call with candidate string containing partial malformed syntax, i.e. malformed IP.
557 Some browsers may ignore the syntax error and add it to the SDP regardless.
558
559 Non-Testable
560 4.3.2. addIceCandidate
561 4.6. (The steps are non-testable because the abort step in enqueue operation
562 steps in before they can reach here):
563 1. If candidate could not be successfully added the user agent MUST
564 queue a task that runs the following steps:
565 1. If connection's [[isClosed]] slot is true, then abort
566 these steps.
567
568 2. If candidate is applied successfully, the user agent MUST queue
569 a task that runs the following steps:
570 1. If connection's [[isClosed]] slot is true, then abort these steps.
571
572 Issues
573 w3c/webrtc-pc#1213
574 addIceCandidate end of candidates woes
575
576 w3c/webrtc-pc#1216
577 Clarify addIceCandidate behavior when adding candidate after end of candidate
578
579 w3c/webrtc-pc#1227
580 addIceCandidate may add ice candidate to the wrong remote description
581
582 w3c/webrtc-pc#1345
583 Make promise rejection/enqueing consistent
584
585 Coverage Report
586 Total: 23
587 Tested: 19
588 Not Tested: 2
589 Non-Testable: 2
590 */
Soares Chenb20cf6b2017-05-19 12:28:43591</script>