| <!doctype html> |
| <meta charset=utf-8> |
| <meta name="timeout" content="long"> |
| <title>RTCDataChannel.prototype.close</title> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <script src="RTCPeerConnection-helper.js"></script> |
| <script> |
| 'use strict'; |
| |
| for (const options of [{}, {negotiated: true, id: 0}]) { |
| const mode = `${options.negotiated? "negotiated " : ""}datachannel`; |
| |
| promise_test(async t => { |
| const [channel1, channel2] = await createDataChannelPair(t, options); |
| const haveClosed = new Promise(r => channel2.onclose = r); |
| let closingSeen = false; |
| channel1.onclosing = t.unreached_func(); |
| channel2.onclosing = () => { |
| assert_equals(channel2.readyState, 'closing'); |
| closingSeen = true; |
| }; |
| channel2.addEventListener('error', t.unreached_func()); |
| channel1.close(); |
| await haveClosed; |
| assert_equals(channel2.readyState, 'closed'); |
| assert_true(closingSeen, 'Closing event was seen'); |
| }, `Close ${mode} causes onclosing and onclose to be called`); |
| |
| promise_test(async t => { |
| // This is the same test as above, but using addEventListener |
| // rather than the "onclose" attribute. |
| const [channel1, channel2] = await createDataChannelPair(t, options); |
| const haveClosed = new Promise(r => channel2.addEventListener('close', r)); |
| let closingSeen = false; |
| channel1.addEventListener('closing', t.unreached_func()); |
| channel2.addEventListener('closing', () => { |
| assert_equals(channel2.readyState, 'closing'); |
| closingSeen = true; |
| }); |
| channel2.addEventListener('error', t.unreached_func()); |
| channel1.close(); |
| await haveClosed; |
| assert_equals(channel2.readyState, 'closed'); |
| assert_true(closingSeen, 'Closing event was seen'); |
| }, `Close ${mode} causes closing and close event to be called`); |
| |
| promise_test(async t => { |
| const pc1 = new RTCPeerConnection(); |
| t.add_cleanup(() => pc1.close()); |
| const [channel1, channel2] = await createDataChannelPair(t, options, pc1); |
| const events = []; |
| let error = null; |
| channel2.addEventListener('error', t.step_func(event => { |
| events.push('error'); |
| assert_true(event instanceof RTCErrorEvent); |
| error = event.error; |
| })); |
| const haveClosed = new Promise(r => channel2.addEventListener('close', () => { |
| events.push('close'); |
| r(); |
| })); |
| pc1.close(); |
| await haveClosed; |
| // Error should fire before close. |
| assert_array_equals(events, ['error', 'close']); |
| assert_true(error instanceof RTCError); |
| assert_equals(error.name, 'OperationError'); |
| assert_equals(error.errorDetail, 'sctp-failure'); |
| // Closing a peerconnection causes a DTLS close, but this event has |
| // no corresponding SCTP cause code. |
| assert_equals(error.sctpCauseCode, null); |
| }, `Close peerconnection causes close event and error to be called on ${mode}`); |
| |
| promise_test(async t => { |
| let pc1 = new RTCPeerConnection(); |
| t.add_cleanup(() => pc1.close()); |
| let [channel1, channel2] = await createDataChannelPair(t, options, pc1); |
| // The expected sequence of events when closing a DC is that |
| // channel1 goes to closing, channel2 fires onclose, and when |
| // the close is confirmed, channel1 fires onclose. |
| // After that, no more events should fire. |
| channel1.onerror = t.unreached_func(); |
| let close2Handler = new Promise(resolve => { |
| channel2.onclose = event => { |
| resolve(); |
| }; |
| }); |
| let close1Handler = new Promise(resolve => { |
| channel1.onclose = event => { |
| resolve(); |
| }; |
| }); |
| channel1.close(); |
| await close2Handler; |
| await close1Handler; |
| channel1.onclose = t.unreached_func(); |
| channel2.onclose = t.unreached_func(); |
| channel2.onerror = t.unreached_func(); |
| pc1.close(); |
| await new Promise(resolve => t.step_timeout(resolve, 10)); |
| }, `Close peerconnection after ${mode} close causes no events`); |
| } |
| </script> |