blob: 711027439b3ab4b38a18d91a2c9a1eaee7e4b3af [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
15// doSignalingHandshake
16// 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);
72 await doSignalingHandshake(pc1, pc2);
73
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);
104 await doSignalingHandshake(pc1, pc2);
105
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);
140 await doSignalingHandshake(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);
171 await doSignalingHandshake(pc1, pc2);
172
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);
210 await doSignalingHandshake(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;
227 readonly attribute RTCPriorityType priority;
228 readonly attribute RTCDataChannelState readyState;
229 ...
230 };
Soares Chen6d8970d2017-06-12 16:02:23231
youennf0e97d092019-03-23 18:02:56232 When an underlying data transport is to be announced (the other peer created a channel with
233 negotiated unset or set to false), the user agent of the peer that did not initiate the
234 creation process MUST queue a task to run the following steps:
235 2. Let channel be a newly created RTCDataChannel object.
236 3. Let configuration be an information bundle received from the other peer as a part of the
237 process to establish the underlying data transport described by the WebRTC DataChannel
238 Protocol specification [RTCWEB-DATA-PROTOCOL].
239 4. Initialize channel's [[DataChannelLabel]], [[Ordered]], [[MaxPacketLifeTime]],
240 [[MaxRetransmits]], [[DataChannelProtocol]], and [[DataChannelId]] internal slots to the
241 corresponding values in configuration.
242 5. Initialize channel's [[Negotiated]] internal slot to false.
243 6. Initialize channel's [[DataChannelPriority]] internal slot based on the integer priority
244 value in configuration, [...]
245 7. Set channel's [[ReadyState]] slot to connecting.
246 8. Fire a datachannel event named datachannel with channel at the RTCPeerConnection object.
Soares Chen6d8970d2017-06-12 16:02:23247
youennf0e97d092019-03-23 18:02:56248 Note: More exhaustive tests are defined in RTCDataChannel-dcep
249 */
Soares Chen6d8970d2017-06-12 16:02:23250
youennf0e97d092019-03-23 18:02:56251promise_test(async (t) => {
252 const resolver = new Resolver();
253 const pc1 = new RTCPeerConnection();
254 const pc2 = new RTCPeerConnection();
255 t.add_cleanup(() => pc1.close());
256 t.add_cleanup(() => pc2.close());
257
258 const dc1 = pc1.createDataChannel('test', {
259 ordered: false,
260 maxRetransmits: 1,
261 protocol: 'custom',
262 priority: 'high'
263 });
264
265 assert_equals(dc1.label, 'test');
266 assert_equals(dc1.ordered, false);
267 assert_equals(dc1.maxPacketLifeTime, null);
268 assert_equals(dc1.maxRetransmits, 1);
269 assert_equals(dc1.protocol, 'custom');
270 assert_equals(dc1.negotiated, false);
271 assert_equals(dc1.priority, 'high');
272
273 pc2.ondatachannel = t.step_func((event) => {
274 const dc2 = event.channel;
275 assert_true(dc2 instanceof RTCDataChannel,
276 'Expect channel to be instance of RTCDataChannel');
277
278 assert_equals(dc2.label, 'test');
279 assert_equals(dc2.ordered, false);
280 assert_equals(dc2.maxPacketLifeTime, null);
281 assert_equals(dc2.maxRetransmits, 1);
282 assert_equals(dc2.protocol, 'custom');
283 assert_equals(dc2.negotiated, false);
284 assert_equals(dc2.id, dc1.id);
285 assert_equals(dc2.priority, 'high');
286
287 resolver.resolve();
288 });
289
290 exchangeIceCandidates(pc1, pc2);
291 await doSignalingHandshake(pc1, pc2);
292
293 await resolver;
294}, 'In-band negotiated channel created on remote peer should match the same configuration as local ' +
295 'peer');
296
297promise_test(async (t) => {
298 const resolver = new Resolver();
299 const pc1 = new RTCPeerConnection();
300 const pc2 = new RTCPeerConnection();
301 t.add_cleanup(() => pc1.close());
302 t.add_cleanup(() => pc2.close());
303
304 const dc1 = pc1.createDataChannel('');
305
306 assert_equals(dc1.label, '');
307 assert_equals(dc1.ordered, true);
308 assert_equals(dc1.maxPacketLifeTime, null);
309 assert_equals(dc1.maxRetransmits, null);
310 assert_equals(dc1.protocol, '');
311 assert_equals(dc1.negotiated, false);
312 assert_equals(dc1.priority, 'low');
313
314 pc2.ondatachannel = t.step_func((event) => {
315 const dc2 = event.channel;
316 assert_true(dc2 instanceof RTCDataChannel,
317 'Expect channel to be instance of RTCDataChannel');
318
319 assert_equals(dc2.label, '');
320 assert_equals(dc2.ordered, true);
321 assert_equals(dc2.maxPacketLifeTime, null);
322 assert_equals(dc2.maxRetransmits, null);
323 assert_equals(dc2.protocol, '');
324 assert_equals(dc2.negotiated, false);
325 assert_equals(dc2.id, dc1.id);
326 assert_equals(dc2.priority, 'low');
327
328 resolver.resolve();
329 });
330
331 exchangeIceCandidates(pc1, pc2);
332 await doSignalingHandshake(pc1, pc2);
333
334 await resolver;
335}, 'In-band negotiated channel created on remote peer should match the same (default) ' +
336 'configuration as local peer');
337
338/*
339 6.2. RTCDataChannel
340 Dictionary RTCDataChannelInit Members
341 negotiated
342 The default value of false tells the user agent to announce the
343 channel in-band and instruct the other peer to dispatch a corresponding
344 RTCDataChannel object. If set to true, it is up to the application
345 to negotiate the channel and create a RTCDataChannel object with the
346 same id at the other peer.
347 */
348promise_test(async (t) => {
349 const resolver = new Resolver();
350 const pc1 = new RTCPeerConnection();
351 const pc2 = new RTCPeerConnection();
352 t.add_cleanup(() => pc1.close());
353 t.add_cleanup(() => pc2.close());
354
355 pc2.ondatachannel = t.unreached_func('datachannel event should not be fired');
356
357 pc1.createDataChannel('test', {
358 negotiated: true
359 });
360
361 exchangeIceCandidates(pc1, pc2);
362 await doSignalingHandshake(pc1, pc2);
363
364 // Wait a bit to ensure the 'datachannel' event does NOT fire
365 t.step_timeout(() => resolver.resolve(), 500);
366 await resolver;
367}, 'Negotiated channel should not fire datachannel event on remote peer');
368
369/*
370 Non-testable
371 6.2. RTCDataChannel
372 When an underlying data transport is to be announced
373 1. If the associated RTCPeerConnection object's [[isClosed]] slot
374 is true, abort these steps.
375
376 The above step is not testable because to reach it we would have to
377 close the peer connection just between receiving the in-band negotiated data
378 channel via DCEP and firing the datachannel event.
379 */
Soares Chen6d8970d2017-06-12 16:02:23380</script>