| <!doctype html> |
| <meta charset=utf-8> |
| <title>RTCPeerConnection.prototype.ondatachannel</title> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <script src="RTCPeerConnection-helper.js"></script> |
| <script> |
| 'use strict'; |
| |
| // Test is based on the following editor draft: |
| // https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.html |
| |
| // The following helper functions are called from RTCPeerConnection-helper.js: |
| // exchangeIceCandidates |
| // doSignalingHandshake |
| |
| /* |
| 6.2. RTCDataChannel |
| When an underlying data transport is to be announced |
| 2. Let channel be a newly created RTCDataChannel object. |
| 5. Set channel's readyState attribute to connecting. |
| 6. Fire a datachannel event named datachannel with channel at the |
| RTCPeerConnection object. |
| |
| 6.3. RTCDataChannelEvent |
| Firing a datachannel event named e with a RTCDataChannel channel means |
| that an event with the name e, which does not bubble (except where |
| otherwise stated) and is not cancelable (except where otherwise stated), |
| and which uses the RTCDataChannelEvent interface with the channel |
| attribute set to channel, must be created and dispatched at the given |
| target. |
| |
| interface RTCDataChannelEvent : Event { |
| readonly attribute RTCDataChannel channel; |
| }; |
| */ |
| async_test(t => { |
| const localPc = new RTCPeerConnection(); |
| t.add_cleanup(() => localPc.close()); |
| const remotePc = new RTCPeerConnection(); |
| |
| t.add_cleanup(() => remotePc.close()); |
| |
| let eventCount = 0; |
| |
| const onDataChannel = t.step_func_done(event => { |
| eventCount++; |
| assert_equals(eventCount, 1, |
| 'Expect data channel event to fire exactly once'); |
| |
| assert_true(event instanceof RTCDataChannelEvent, |
| 'Expect event to be instance of RTCDataChannelEvent'); |
| |
| assert_equals(event.bubbles, false); |
| assert_equals(event.cancelable, false); |
| |
| const { channel } = event; |
| assert_true(channel instanceof RTCDataChannel, |
| 'Expect channel to be instance of RTCDataChannel'); |
| |
| const { readyState } = channel; |
| |
| // The spec requires readyState to be connecting at first, |
| // but it may quickly change to open before the callback |
| // is invoked, especially with local connections. |
| assert_true(readyState === 'connecting' || readyState === 'open', |
| 'Expect channel ready state to be either connecting or open'); |
| }); |
| |
| localPc.createDataChannel('test'); |
| |
| remotePc.addEventListener('datachannel', onDataChannel); |
| exchangeIceCandidates(localPc, remotePc); |
| doSignalingHandshake(localPc, remotePc); |
| }, 'datachannel event should fire when new data channel is announced to the remote peer'); |
| |
| /* |
| 6.2. RTCDataChannel |
| interface RTCDataChannel : EventTarget { |
| readonly attribute USVString label; |
| readonly attribute boolean ordered; |
| readonly attribute unsigned short? maxPacketLifeTime; |
| readonly attribute unsigned short? maxRetransmits; |
| readonly attribute USVString protocol; |
| readonly attribute boolean negotiated; |
| readonly attribute unsigned short? id; |
| readonly attribute RTCPriorityType priority; |
| readonly attribute RTCDataChannelState readyState; |
| ... |
| }; |
| |
| When an underlying data transport is to be announced |
| 3. Let configuration be an information bundle received from the |
| other peer as a part of the process to establish the underlying |
| data transport described by the WebRTC DataChannel Protocol |
| specification [RTCWEB-DATA-PROTOCOL]. |
| 4. Initialize channel's label, ordered, maxPacketLifeTime, |
| maxRetransmits, protocol, negotiated and id attributes to their |
| corresponding values in configuration. |
| */ |
| async_test(t => { |
| const localPc = new RTCPeerConnection(); |
| t.add_cleanup(() => localPc.close()); |
| const remotePc = new RTCPeerConnection(); |
| |
| t.add_cleanup(() => remotePc.close()); |
| |
| const onDataChannel = t.step_func_done(event => { |
| const remoteChannel = event.channel; |
| assert_true(remoteChannel instanceof RTCDataChannel, |
| 'Expect channel to be instance of RTCDataChannel'); |
| |
| assert_equals(remoteChannel.label, 'test'); |
| assert_equals(remoteChannel.id, 8); |
| assert_equals(remoteChannel.ordered, false); |
| assert_equals(remoteChannel.maxRetransmits, 1); |
| assert_equals(remoteChannel.protocol, 'custom'); |
| assert_equals(remoteChannel.priority, 'high'); |
| }); |
| |
| const localChannel = localPc.createDataChannel('test', { |
| id: 8, |
| ordered: false, |
| maxRetransmits: 1, |
| protocol: 'custom', |
| priority: 'high' |
| }); |
| |
| assert_equals(localChannel.label, 'test'); |
| assert_equals(localChannel.id, 8); |
| assert_equals(localChannel.ordered, false); |
| assert_equals(localChannel.maxRetransmits, 1); |
| assert_equals(localChannel.protocol, 'custom'); |
| assert_equals(localChannel.priority, 'high'); |
| |
| remotePc.addEventListener('datachannel', onDataChannel); |
| exchangeIceCandidates(localPc, remotePc); |
| doSignalingHandshake(localPc, remotePc); |
| }, 'Data channel created on remote peer should match the same configuration as local peer'); |
| |
| /* |
| 6.2. RTCDataChannel |
| Dictionary RTCDataChannelInit Members |
| negotiated |
| The default value of false tells the user agent to announce the |
| channel in-band and instruct the other peer to dispatch a corresponding |
| RTCDataChannel object. If set to true, it is up to the application |
| to negotiate the channel and create a RTCDataChannel object with the |
| same id at the other peer. |
| */ |
| async_test(t => { |
| const localPc = new RTCPeerConnection(); |
| t.add_cleanup(() => localPc.close()); |
| const remotePc = new RTCPeerConnection(); |
| |
| t.add_cleanup(() => remotePc.close()); |
| |
| const onDataChannel = t.unreached_func('datachannel event should not be fired'); |
| |
| localPc.createDataChannel('test', { |
| negotiated: true |
| }); |
| |
| remotePc.addEventListener('datachannel', onDataChannel); |
| exchangeIceCandidates(localPc, remotePc); |
| doSignalingHandshake(localPc, remotePc); |
| |
| t.step_timeout(t.step_func_done(), 200); |
| }, 'Data channel created with negotiated set to true should not fire datachannel event on remote peer'); |
| |
| /* |
| Non-testable |
| 6.2. RTCDataChannel |
| When an underlying data transport is to be announced |
| 1. If the associated RTCPeerConnection object's [[isClosed]] slot |
| is true, abort these steps. |
| |
| The above step is not testable because to reach it we would have to |
| close the peer connection after the remote peer call |
| setLocalDescription(answer) but before the underlying data transport |
| is connected. This require the promise callback for setLocalDescription() |
| to be called at the right moment, which is not always possible. |
| */ |
| </script> |