blob: 034365508b6bca14698537819d86c53fc1226d5e [file] [log] [blame]
Soares Chen6d8970d2017-06-12 16:02:231<!doctype html>
2<meta charset=utf-8>
3<title>RTCPeerConnection.prototype.ondatachannel</title>
4<script src="/resources/testharness.js"></script>
5<script src="/resources/testharnessreport.js"></script>
6<script src="RTCPeerConnection-helper.js"></script>
7<script>
youennf0e97d092019-03-23 18:02:568'use strict';
Soares Chen6d8970d2017-06-12 16:02:239
youennf0e97d092019-03-23 18:02:5610// Test is based on the following revision:
11// https://rawgit.com/w3c/webrtc-pc/1cc5bfc3ff18741033d804c4a71f7891242fb5b3/webrtc.html
Soares Chen6d8970d2017-06-12 16:02:2312
youennf0e97d092019-03-23 18:02:5613// The following helper functions are called from RTCPeerConnection-helper.js:
14// exchangeIceCandidates
Jan-Ivar Bruaroeye880d972020-05-11 17:30:2415// exchangeOfferAnswer
youennf0e97d092019-03-23 18:02:5616// createDataChannelPair
Soares Chen6d8970d2017-06-12 16:02:2317
youennf0e97d092019-03-23 18:02:5618/*
19 6.2. RTCDataChannel
20 When an underlying data transport is to be announced (the other peer created a channel with
21 negotiated unset or set to false), the user agent of the peer that did not initiate the
22 creation process MUST queue a task to run the following steps:
23 2. Let channel be a newly created RTCDataChannel object.
24 7. Set channel's [[ReadyState]] to open (but do not fire the open event, yet).
25 8. Fire a datachannel event named datachannel with channel at the RTCPeerConnection object.
Soares Chen6d8970d2017-06-12 16:02:2326
youennf0e97d092019-03-23 18:02:5627 6.3. RTCDataChannelEvent
28 Firing a datachannel event named e with an RTCDataChannel channel means that an event with the
29 name e, which does not bubble (except where otherwise stated) and is not cancelable (except
30 where otherwise stated), and which uses the RTCDataChannelEvent interface with the channel
31 attribute set to channel, MUST be created and dispatched at the given target.
Soares Chen6d8970d2017-06-12 16:02:2332
youennf0e97d092019-03-23 18:02:5633 interface RTCDataChannelEvent : Event {
34 readonly attribute RTCDataChannel channel;
35 };
36 */
37promise_test(async (t) => {
38 const resolver = new Resolver();
39 const pc1 = new RTCPeerConnection();
40 const pc2 = new RTCPeerConnection();
41 t.add_cleanup(() => pc1.close());
42 t.add_cleanup(() => pc2.close());
Soares Chen6d8970d2017-06-12 16:02:2343
youennf0e97d092019-03-23 18:02:5644 let eventCount = 0;
Philipp Hancke1622a022018-06-11 10:00:5345
youennf0e97d092019-03-23 18:02:5646 pc2.ondatachannel = t.step_func((event) => {
47 eventCount++;
48 assert_equals(eventCount, 1,
49 'Expect data channel event to fire exactly once');
Soares Chen6d8970d2017-06-12 16:02:2350
youennf0e97d092019-03-23 18:02:5651 assert_true(event instanceof RTCDataChannelEvent,
52 'Expect event to be instance of RTCDataChannelEvent');
Soares Chen6d8970d2017-06-12 16:02:2353
youennf0e97d092019-03-23 18:02:5654 assert_equals(event.bubbles, false);
55 assert_equals(event.cancelable, false);
Soares Chen6d8970d2017-06-12 16:02:2356
youennf0e97d092019-03-23 18:02:5657 const dc = event.channel;
58 assert_true(dc instanceof RTCDataChannel,
59 'Expect channel to be instance of RTCDataChannel');
Soares Chen6d8970d2017-06-12 16:02:2360
youennf0e97d092019-03-23 18:02:5661 // The channel should be in the 'open' state already.
62 // See: https://github.com/w3c/webrtc-pc/pull/1851
63 assert_equals(dc.readyState, 'open',
64 'Expect channel ready state to be open');
Soares Chen6d8970d2017-06-12 16:02:2365
youennf0e97d092019-03-23 18:02:5666 resolver.resolve();
67 });
Soares Chen6d8970d2017-06-12 16:02:2368
youennf0e97d092019-03-23 18:02:5669 pc1.createDataChannel('fire-me!');
70
71 exchangeIceCandidates(pc1, pc2);
Jan-Ivar Bruaroeye880d972020-05-11 17:30:2472 await exchangeOfferAnswer(pc1, pc2);
youennf0e97d092019-03-23 18:02:5673
74 await resolver;
75}, 'Data channel event should fire when new data channel is announced to the remote peer');
76
77/*
78 Since the channel should be in the 'open' state when dispatching via the 'datachannel' event,
79 we should be able to send data in the event handler.
80 */
81promise_test(async (t) => {
82 const resolver = new Resolver();
83 const pc1 = new RTCPeerConnection();
84 const pc2 = new RTCPeerConnection();
85 t.add_cleanup(() => pc1.close());
86 t.add_cleanup(() => pc2.close());
87
88 const message = 'meow meow!';
89
90 pc2.ondatachannel = t.step_func((event) => {
91 const dc2 = event.channel;
92 dc2.send(message);
93 });
94
95 const dc1 = pc1.createDataChannel('fire-me!');
96 dc1.onmessage = t.step_func((event) => {
97 assert_equals(event.data, message,
98 'Received data should be equal to sent data');
99
100 resolver.resolve();
101 });
102
103 exchangeIceCandidates(pc1, pc2);
Jan-Ivar Bruaroeye880d972020-05-11 17:30:24104 await exchangeOfferAnswer(pc1, pc2);
youennf0e97d092019-03-23 18:02:56105
106 await resolver;
107}, 'Should be able to send data in a datachannel event handler');
108
109/*
110 6.2. RTCDataChannel
111 When an underlying data transport is to be announced (the other peer created a channel with
112 negotiated unset or set to false), the user agent of the peer that did not initiate the
113 creation process MUST queue a task to run the following steps:
114 8. Fire a datachannel event named datachannel with channel at the RTCPeerConnection object.
115 9. If the channel's [[ReadyState]] is still open, announce the data channel as open.
116 */
117promise_test(async (t) => {
118 const resolver = new Resolver();
119 const pc1 = new RTCPeerConnection();
120 const pc2 = new RTCPeerConnection();
121 t.add_cleanup(() => pc1.close());
122 t.add_cleanup(() => pc2.close());
123
124 pc2.ondatachannel = t.step_func((event) => {
125 const dc = event.channel;
126 dc.onopen = t.step_func(() => {
127 assert_unreached('Open event should not fire');
Soares Chen6d8970d2017-06-12 16:02:23128 });
129
youennf0e97d092019-03-23 18:02:56130 // This should prevent triggering the 'open' event
131 dc.close();
Soares Chen6d8970d2017-06-12 16:02:23132
youennf0e97d092019-03-23 18:02:56133 // Wait a bit to ensure the 'open' event does NOT fire
134 t.step_timeout(() => resolver.resolve(), 500);
135 });
Soares Chen6d8970d2017-06-12 16:02:23136
youennf0e97d092019-03-23 18:02:56137 pc1.createDataChannel('fire-me!');
Soares Chen6d8970d2017-06-12 16:02:23138
youennf0e97d092019-03-23 18:02:56139 exchangeIceCandidates(pc1, pc2);
Jan-Ivar Bruaroeye880d972020-05-11 17:30:24140 await exchangeOfferAnswer(pc1, pc2);
Soares Chen6d8970d2017-06-12 16:02:23141
youennf0e97d092019-03-23 18:02:56142 await resolver;
143}, 'Open event should not be raised when closing the channel in the datachannel event');
Philipp Hancke1622a022018-06-11 10:00:53144
youennf0e97d092019-03-23 18:02:56145// Added this test as a result of the discussion in
146// https://github.com/w3c/webrtc-pc/pull/1851#discussion_r185976747
147promise_test(async (t) => {
148 const resolver = new Resolver();
149 const pc1 = new RTCPeerConnection();
150 const pc2 = new RTCPeerConnection();
151 t.add_cleanup(() => pc1.close());
152 t.add_cleanup(() => pc2.close());
Soares Chen6d8970d2017-06-12 16:02:23153
youennf0e97d092019-03-23 18:02:56154 pc2.ondatachannel = t.step_func((event) => {
155 const dc = event.channel;
156 dc.onopen = t.step_func((event) => {
157 resolver.resolve();
Soares Chen6d8970d2017-06-12 16:02:23158 });
159
youennf0e97d092019-03-23 18:02:56160 // This should NOT prevent triggering the 'open' event since it enqueues at least two tasks
161 t.step_timeout(() => {
162 t.step_timeout(() => {
163 dc.close()
164 }, 1);
165 }, 1);
166 });
167
168 pc1.createDataChannel('fire-me!');
169
170 exchangeIceCandidates(pc1, pc2);
Jan-Ivar Bruaroeye880d972020-05-11 17:30:24171 await exchangeOfferAnswer(pc1, pc2);
youennf0e97d092019-03-23 18:02:56172
173 await resolver;
174}, 'Open event should be raised when closing the channel in the datachannel event after ' +
175 'enqueuing a task');
176
177
178/*
179 Combination of the two tests above (send and close).
180 */
181promise_test(async (t) => {
182 const resolver = new Resolver();
183 const pc1 = new RTCPeerConnection();
184 const pc2 = new RTCPeerConnection();
185 t.add_cleanup(() => pc1.close());
186 t.add_cleanup(() => pc2.close());
187
188 const message = 'meow meow!';
189
190 pc2.ondatachannel = t.step_func((event) => {
191 const dc2 = event.channel;
192 dc2.onopen = t.step_func(() => {
193 assert_unreached('Open event should not fire');
Soares Chen6d8970d2017-06-12 16:02:23194 });
195
youennf0e97d092019-03-23 18:02:56196 // This should send but still prevent triggering the 'open' event
197 dc2.send(message);
198 dc2.close();
199 });
Soares Chen6d8970d2017-06-12 16:02:23200
youennf0e97d092019-03-23 18:02:56201 const dc1 = pc1.createDataChannel('fire-me!');
202 dc1.onmessage = t.step_func((event) => {
203 assert_equals(event.data, message,
204 'Received data should be equal to sent data');
Soares Chen6d8970d2017-06-12 16:02:23205
youennf0e97d092019-03-23 18:02:56206 resolver.resolve();
207 });
Soares Chen6d8970d2017-06-12 16:02:23208
youennf0e97d092019-03-23 18:02:56209 exchangeIceCandidates(pc1, pc2);
Jan-Ivar Bruaroeye880d972020-05-11 17:30:24210 await exchangeOfferAnswer(pc1, pc2);
Philipp Hancke1622a022018-06-11 10:00:53211
youennf0e97d092019-03-23 18:02:56212 await resolver;
213}, 'Open event should not be raised when sending and immediately closing the channel in the ' +
214 'datachannel event');
Soares Chen6d8970d2017-06-12 16:02:23215
youennf0e97d092019-03-23 18:02:56216/*
217 6.2. RTCDataChannel
Soares Chen6d8970d2017-06-12 16:02:23218
youennf0e97d092019-03-23 18:02:56219 interface RTCDataChannel : EventTarget {
220 readonly attribute USVString label;
221 readonly attribute boolean ordered;
222 readonly attribute unsigned short? maxPacketLifeTime;
223 readonly attribute unsigned short? maxRetransmits;
224 readonly attribute USVString protocol;
225 readonly attribute boolean negotiated;
226 readonly attribute unsigned short? id;
youennf0e97d092019-03-23 18:02:56227 readonly attribute RTCDataChannelState readyState;
228 ...
229 };
Soares Chen6d8970d2017-06-12 16:02:23230
youennf0e97d092019-03-23 18:02:56231 When an underlying data transport is to be announced (the other peer created a channel with
232 negotiated unset or set to false), the user agent of the peer that did not initiate the
233 creation process MUST queue a task to run the following steps:
234 2. Let channel be a newly created RTCDataChannel object.
235 3. Let configuration be an information bundle received from the other peer as a part of the
236 process to establish the underlying data transport described by the WebRTC DataChannel
237 Protocol specification [RTCWEB-DATA-PROTOCOL].
238 4. Initialize channel's [[DataChannelLabel]], [[Ordered]], [[MaxPacketLifeTime]],
239 [[MaxRetransmits]], [[DataChannelProtocol]], and [[DataChannelId]] internal slots to the
240 corresponding values in configuration.
241 5. Initialize channel's [[Negotiated]] internal slot to false.
youennf0e97d092019-03-23 18:02:56242 7. Set channel's [[ReadyState]] slot to connecting.
243 8. Fire a datachannel event named datachannel with channel at the RTCPeerConnection object.
Soares Chen6d8970d2017-06-12 16:02:23244
youennf0e97d092019-03-23 18:02:56245 Note: More exhaustive tests are defined in RTCDataChannel-dcep
246 */
Soares Chen6d8970d2017-06-12 16:02:23247
youennf0e97d092019-03-23 18:02:56248promise_test(async (t) => {
249 const resolver = new Resolver();
250 const pc1 = new RTCPeerConnection();
251 const pc2 = new RTCPeerConnection();
252 t.add_cleanup(() => pc1.close());
253 t.add_cleanup(() => pc2.close());
254
255 const dc1 = pc1.createDataChannel('test', {
256 ordered: false,
257 maxRetransmits: 1,
Dominique Hazael-Massieux24221e02020-09-30 14:08:28258 protocol: 'custom'
youennf0e97d092019-03-23 18:02:56259 });
260
261 assert_equals(dc1.label, 'test');
262 assert_equals(dc1.ordered, false);
263 assert_equals(dc1.maxPacketLifeTime, null);
264 assert_equals(dc1.maxRetransmits, 1);
265 assert_equals(dc1.protocol, 'custom');
266 assert_equals(dc1.negotiated, false);
youennf0e97d092019-03-23 18:02:56267
268 pc2.ondatachannel = t.step_func((event) => {
269 const dc2 = event.channel;
270 assert_true(dc2 instanceof RTCDataChannel,
271 'Expect channel to be instance of RTCDataChannel');
272
273 assert_equals(dc2.label, 'test');
274 assert_equals(dc2.ordered, false);
275 assert_equals(dc2.maxPacketLifeTime, null);
276 assert_equals(dc2.maxRetransmits, 1);
277 assert_equals(dc2.protocol, 'custom');
278 assert_equals(dc2.negotiated, false);
279 assert_equals(dc2.id, dc1.id);
youennf0e97d092019-03-23 18:02:56280
281 resolver.resolve();
282 });
283
284 exchangeIceCandidates(pc1, pc2);
Jan-Ivar Bruaroeye880d972020-05-11 17:30:24285 await exchangeOfferAnswer(pc1, pc2);
youennf0e97d092019-03-23 18:02:56286
287 await resolver;
288}, 'In-band negotiated channel created on remote peer should match the same configuration as local ' +
289 'peer');
290
291promise_test(async (t) => {
292 const resolver = new Resolver();
293 const pc1 = new RTCPeerConnection();
294 const pc2 = new RTCPeerConnection();
295 t.add_cleanup(() => pc1.close());
296 t.add_cleanup(() => pc2.close());
297
298 const dc1 = pc1.createDataChannel('');
299
300 assert_equals(dc1.label, '');
301 assert_equals(dc1.ordered, true);
302 assert_equals(dc1.maxPacketLifeTime, null);
303 assert_equals(dc1.maxRetransmits, null);
304 assert_equals(dc1.protocol, '');
305 assert_equals(dc1.negotiated, false);
youennf0e97d092019-03-23 18:02:56306
307 pc2.ondatachannel = t.step_func((event) => {
308 const dc2 = event.channel;
309 assert_true(dc2 instanceof RTCDataChannel,
310 'Expect channel to be instance of RTCDataChannel');
311
312 assert_equals(dc2.label, '');
313 assert_equals(dc2.ordered, true);
314 assert_equals(dc2.maxPacketLifeTime, null);
315 assert_equals(dc2.maxRetransmits, null);
316 assert_equals(dc2.protocol, '');
317 assert_equals(dc2.negotiated, false);
318 assert_equals(dc2.id, dc1.id);
youennf0e97d092019-03-23 18:02:56319
320 resolver.resolve();
321 });
322
323 exchangeIceCandidates(pc1, pc2);
Jan-Ivar Bruaroeye880d972020-05-11 17:30:24324 await exchangeOfferAnswer(pc1, pc2);
youennf0e97d092019-03-23 18:02:56325
326 await resolver;
327}, 'In-band negotiated channel created on remote peer should match the same (default) ' +
328 'configuration as local peer');
329
330/*
331 6.2. RTCDataChannel
332 Dictionary RTCDataChannelInit Members
333 negotiated
334 The default value of false tells the user agent to announce the
335 channel in-band and instruct the other peer to dispatch a corresponding
336 RTCDataChannel object. If set to true, it is up to the application
337 to negotiate the channel and create a RTCDataChannel object with the
338 same id at the other peer.
339 */
340promise_test(async (t) => {
341 const resolver = new Resolver();
342 const pc1 = new RTCPeerConnection();
343 const pc2 = new RTCPeerConnection();
344 t.add_cleanup(() => pc1.close());
345 t.add_cleanup(() => pc2.close());
346
347 pc2.ondatachannel = t.unreached_func('datachannel event should not be fired');
348
349 pc1.createDataChannel('test', {
Harald Alvestrandf6cf3bd2019-04-04 12:39:57350 negotiated: true,
351 id: 42
youennf0e97d092019-03-23 18:02:56352 });
353
354 exchangeIceCandidates(pc1, pc2);
Jan-Ivar Bruaroeye880d972020-05-11 17:30:24355 await exchangeOfferAnswer(pc1, pc2);
youennf0e97d092019-03-23 18:02:56356
357 // Wait a bit to ensure the 'datachannel' event does NOT fire
358 t.step_timeout(() => resolver.resolve(), 500);
359 await resolver;
360}, 'Negotiated channel should not fire datachannel event on remote peer');
361
362/*
363 Non-testable
364 6.2. RTCDataChannel
365 When an underlying data transport is to be announced
366 1. If the associated RTCPeerConnection object's [[isClosed]] slot
367 is true, abort these steps.
368
369 The above step is not testable because to reach it we would have to
370 close the peer connection just between receiving the in-band negotiated data
371 channel via DCEP and firing the datachannel event.
372 */
Soares Chen6d8970d2017-06-12 16:02:23373</script>