mirror of
https://github.com/servo/servo.git
synced 2025-06-30 12:03:38 +01:00
438 lines
15 KiB
HTML
438 lines
15 KiB
HTML
<!doctype html>
|
|
<meta charset=utf-8>
|
|
<title>RTCPeerConnection.prototype.createDataChannel</title>
|
|
<script src=/resources/testharness.js></script>
|
|
<script src=/resources/testharnessreport.js></script>
|
|
<script>
|
|
'use strict';
|
|
|
|
// Test is based on the following editor draft:
|
|
// https://rawgit.com/w3c/webrtc-pc/cc8d80f455b86c8041d63bceb8b457f45c72aa89/webrtc.html
|
|
|
|
/*
|
|
6.1. RTCPeerConnection Interface Extensions
|
|
|
|
partial interface RTCPeerConnection {
|
|
RTCDataChannel createDataChannel(USVString label,
|
|
optional RTCDataChannelInit dataChannelDict);
|
|
...
|
|
};
|
|
|
|
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;
|
|
};
|
|
|
|
dictionary RTCDataChannelInit {
|
|
boolean ordered = true;
|
|
unsigned short maxPacketLifeTime;
|
|
unsigned short maxRetransmits;
|
|
USVString protocol = "";
|
|
boolean negotiated = false;
|
|
[EnforceRange]
|
|
unsigned short id;
|
|
RTCPriorityType priority = "low";
|
|
};
|
|
|
|
4.9.1. RTCPriorityType Enum
|
|
|
|
enum RTCPriorityType {
|
|
"very-low",
|
|
"low",
|
|
"medium",
|
|
"high"
|
|
};
|
|
*/
|
|
|
|
test(() => {
|
|
const pc = new RTCPeerConnection();
|
|
assert_equals(pc.createDataChannel.length, 1);
|
|
assert_throws(new TypeError(), () => pc.createDataChannel());
|
|
}, 'createDataChannel with no argument should throw TypeError');
|
|
|
|
/*
|
|
6.2. createDataChannel
|
|
2. If connection's [[isClosed]] slot is true, throw an InvalidStateError.
|
|
*/
|
|
test(() => {
|
|
const pc = new RTCPeerConnection();
|
|
pc.close();
|
|
assert_equals(pc.signalingState, 'closed', 'signaling state');
|
|
assert_throws('InvalidStateError', () => pc.createDataChannel(''));
|
|
}, 'createDataChannel with closed connection should throw InvalidStateError');
|
|
|
|
/*
|
|
6.1. createDataChannel
|
|
4. Let channel have a [[Label]] internal slot initialized to the value of the
|
|
first argument.
|
|
5. Let options be the second argument.
|
|
6. Let channel have an [[MaxPacketLifeTime]] internal slot initialized to
|
|
option's maxPacketLifeTime member, if present, otherwise null.
|
|
7. Let channel have an [[MaxRetransmits]] internal slot initialized to
|
|
option's maxRetransmits member, if present, otherwise null.
|
|
8. Let channel have an [[DataChannelId]] internal slot initialized to
|
|
option's id member, if present, otherwise null.
|
|
9. Let channel have an [[Ordered]] internal slot initialized to option's
|
|
ordered member.
|
|
10. Let channel have an [[Protocol]] internal slot initialized to option's
|
|
protocol member.
|
|
11. Let channel have an [[Negotiated]] internal slot initialized to option's
|
|
negotiated member.
|
|
12. Let channel have an [[DataChannelPriority]] internal slot initialized
|
|
to option's priority member.
|
|
|
|
6.2. RTCDataChannel
|
|
|
|
A RTCDataChannel, created with createDataChannel or dispatched via a
|
|
RTCDataChannelEvent, MUST initially be in the connecting state
|
|
|
|
binaryType
|
|
When a RTCDataChannel object is created, the binaryType attribute MUST
|
|
be initialized to the string "blob".
|
|
*/
|
|
test(() => {
|
|
const pc = new RTCPeerConnection();
|
|
const channel = pc.createDataChannel('');
|
|
assert_true(channel instanceof RTCDataChannel, 'is RTCDataChannel');
|
|
assert_equals(channel.label, '');
|
|
assert_equals(channel.ordered, true);
|
|
assert_equals(channel.maxPacketLifeTime, null);
|
|
assert_equals(channel.maxRetransmits, null);
|
|
assert_equals(channel.protocol, '');
|
|
assert_equals(channel.negotiated, false);
|
|
|
|
// Since no offer/answer exchange has occurred yet, the DTLS role is unknown
|
|
// and so the ID should be null.
|
|
assert_equals(channel.id, null);
|
|
assert_equals(channel.priority, 'low');
|
|
|
|
assert_equals(channel.readyState, 'connecting');
|
|
assert_equals(channel.binaryType, 'blob');
|
|
|
|
}, 'createDataChannel attribute default values');
|
|
|
|
test(() => {
|
|
const pc = new RTCPeerConnection();
|
|
const channel = pc.createDataChannel('test', {
|
|
ordered: false,
|
|
maxPacketLifeTime: null,
|
|
maxRetransmits: 1,
|
|
protocol: 'custom',
|
|
negotiated: true,
|
|
id: 3,
|
|
priority: 'high'
|
|
});
|
|
|
|
assert_true(channel instanceof RTCDataChannel, 'is RTCDataChannel');
|
|
assert_equals(channel.label, 'test');
|
|
assert_equals(channel.ordered, false);
|
|
assert_equals(channel.maxPacketLifeTime, null);
|
|
assert_equals(channel.maxRetransmits, 1);
|
|
assert_equals(channel.protocol, 'custom');
|
|
assert_equals(channel.negotiated, true);
|
|
assert_equals(channel.id, 3);
|
|
assert_equals(channel.priority, 'high');
|
|
assert_equals(channel.readyState, 'connecting');
|
|
assert_equals(channel.binaryType, 'blob');
|
|
|
|
}, 'createDataChannel with provided parameters should initialize attributes to provided values');
|
|
|
|
/*
|
|
6.2. createDataChannel
|
|
4. Let channel have a [[Label]] internal slot initialized to the value of the
|
|
first argument.
|
|
|
|
[ECMA262] 7.1.12. ToString(argument)
|
|
undefined -> "undefined"
|
|
null -> "null"
|
|
|
|
[WebIDL] 3.10.15. Convert a DOMString to a sequence of Unicode scalar values
|
|
*/
|
|
const labels = [
|
|
['"foo"', 'foo', 'foo'],
|
|
['null', null, 'null'],
|
|
['undefined', undefined, 'undefined'],
|
|
['lone surrogate', '\uD800', '\uFFFD'],
|
|
];
|
|
for (const [description, label, expected] of labels) {
|
|
test(() => {
|
|
const pc = new RTCPeerConnection;
|
|
const channel = pc.createDataChannel(label);
|
|
assert_equals(channel.label, expected);
|
|
}, `createDataChannel with label ${description} should succeed`);
|
|
}
|
|
|
|
/*
|
|
6.2. RTCDataChannel
|
|
createDataChannel
|
|
9. Let channel have an [[Ordered]] internal slot initialized to option's
|
|
ordered member.
|
|
*/
|
|
test(() => {
|
|
const pc = new RTCPeerConnection();
|
|
const channel = pc.createDataChannel('', { ordered: false });
|
|
assert_equals(channel.ordered, false);
|
|
}, 'createDataChannel with ordered false should succeed');
|
|
|
|
// true as the default value of a boolean is confusing because null is converted
|
|
// to false while undefined is converted to true.
|
|
test(() => {
|
|
const pc = new RTCPeerConnection();
|
|
const channel1 = pc.createDataChannel('', { ordered: null });
|
|
assert_equals(channel1.ordered, false);
|
|
const channel2 = pc.createDataChannel('', { ordered: undefined });
|
|
assert_equals(channel2.ordered, true);
|
|
}, 'createDataChannel with ordered null/undefined should succeed');
|
|
|
|
/*
|
|
6.2. RTCDataChannel
|
|
createDataChannel
|
|
6. Let channel have an [[MaxPacketLifeTime]] internal slot initialized to
|
|
option's maxPacketLifeTime member, if present, otherwise null.
|
|
*/
|
|
test(() => {
|
|
const pc = new RTCPeerConnection;
|
|
const channel = pc.createDataChannel('', { maxPacketLifeTime: 0 });
|
|
assert_equals(channel.maxPacketLifeTime, 0);
|
|
}, 'createDataChannel with maxPacketLifeTime 0 should succeed');
|
|
|
|
/*
|
|
6.2. RTCDataChannel
|
|
createDataChannel
|
|
7. Let channel have an [[MaxRetransmits]] internal slot initialized to
|
|
option's maxRetransmits member, if present, otherwise null.
|
|
*/
|
|
test(() => {
|
|
const pc = new RTCPeerConnection;
|
|
const channel = pc.createDataChannel('', { maxRetransmits: 0 });
|
|
assert_equals(channel.maxRetransmits, 0);
|
|
}, 'createDataChannel with maxRetransmits 0 should succeed');
|
|
|
|
/*
|
|
6.2. createDataChannel
|
|
15. If both [[MaxPacketLifeTime]] and [[MaxRetransmits]] attributes are set
|
|
(not null), throw a TypeError.
|
|
*/
|
|
test(() => {
|
|
const pc = new RTCPeerConnection;
|
|
assert_throws(new TypeError(), () => pc.createDataChannel('', {
|
|
maxPacketLifeTime: 0,
|
|
maxRetransmits: 0
|
|
}));
|
|
}, 'createDataChannel with both maxPacketLifeTime and maxRetransmits should throw SyntaxError');
|
|
|
|
/*
|
|
6.2. RTCDataChannel
|
|
createDataChannel
|
|
10. Let channel have an [[Protocol]] internal slot initialized to option's
|
|
protocol member.
|
|
*/
|
|
const protocols = [
|
|
['"foo"', 'foo', 'foo'],
|
|
['null', null, 'null'],
|
|
['undefined', undefined, ''],
|
|
['lone surrogate', '\uD800', '\uFFFD'],
|
|
];
|
|
for (const [description, protocol, expected] of protocols) {
|
|
test(() => {
|
|
const pc = new RTCPeerConnection;
|
|
const channel = pc.createDataChannel('', { protocol });
|
|
assert_equals(channel.protocol, expected);
|
|
}, `createDataChannel with protocol ${description} should succeed`);
|
|
}
|
|
|
|
/*
|
|
6.2. RTCDataChannel
|
|
createDataChannel
|
|
11. Let channel have an [[Negotiated]] internal slot initialized to option's
|
|
negotiated member.
|
|
*/
|
|
test(() => {
|
|
const pc = new RTCPeerConnection;
|
|
const channel = pc.createDataChannel('', { negotiated: true });
|
|
assert_equals(channel.negotiated, true);
|
|
}, 'createDataChannel with negotiated true should succeed');
|
|
|
|
|
|
/*
|
|
6.2. RTCDataChannel
|
|
createDataChannel
|
|
10. If id is equal to 65535, which is greater than the maximum allowed ID
|
|
of 65534 but still qualifies as an unsigned short, throw a TypeError.
|
|
*/
|
|
for (const id of [0, 1, 65534]) {
|
|
test(() => {
|
|
const pc = new RTCPeerConnection();
|
|
const channel = pc.createDataChannel('', { id });
|
|
assert_equals(channel.id, id);
|
|
}, `createDataChannel with id ${id} should succeed`);
|
|
}
|
|
|
|
for (const id of [-1, 65535, 65536]) {
|
|
test(() => {
|
|
const pc = new RTCPeerConnection();
|
|
assert_throws(new TypeError(), () => pc.createDataChannel('', { id }));
|
|
}, `createDataChannel with id ${id} should throw TypeError`);
|
|
}
|
|
|
|
/*
|
|
6.2. RTCDataChannel
|
|
createDataChannel
|
|
12. Let channel have an [[DataChannelPriority]] internal slot initialized
|
|
to option's priority member.
|
|
|
|
*/
|
|
test(() => {
|
|
const pc = new RTCPeerConnection();
|
|
const channel = pc.createDataChannel('', { priority: 'high' });
|
|
assert_equals(channel.priority, 'high');
|
|
}, 'createDataChannel with priority "high" should succeed');
|
|
|
|
test(() => {
|
|
const pc = new RTCPeerConnection();
|
|
assert_throws(new TypeError(),
|
|
() => pc.createDataChannel('', { priority: 'invalid' }));
|
|
}, 'createDataChannel with invalid priority should throw TypeError');
|
|
|
|
/*
|
|
6.2. createDataChannel
|
|
13. If [[Negotiated]] is false and [[Label]] is longer than 65535 bytes
|
|
long, throw a TypeError. */
|
|
test(() => {
|
|
const pc = new RTCPeerConnection();
|
|
assert_throws(new TypeError(), () =>
|
|
pc.createDataChannel('', {
|
|
label: ' '.repeat(65536),
|
|
negotiated: false
|
|
}));
|
|
}, 'createDataChannel with negotiated false and long label should throw TypeError');
|
|
|
|
/*
|
|
6.2. createDataChannel
|
|
14. If [[Negotiated]] is false and [[Protocol]] is longer than 65535 bytes long,
|
|
throw a TypeError.
|
|
*/
|
|
test(() => {
|
|
const pc = new RTCPeerConnection();
|
|
assert_throws(new TypeError(), () =>
|
|
pc.createDataChannel('', {
|
|
protocol: ' '.repeat(65536),
|
|
negotiated: false
|
|
}));
|
|
}, 'createDataChannel with negotiated false and long protocol should throw TypeError');
|
|
|
|
test(() => {
|
|
const pc = new RTCPeerConnection();
|
|
const label = ' '.repeat(65536)
|
|
|
|
const channel = pc.createDataChannel('', {
|
|
label,
|
|
protocol: ' '.repeat(65536),
|
|
negotiated: true
|
|
});
|
|
|
|
assert_equals(channel.label, label);
|
|
}, 'createDataChannel with negotiated true and long label and long protocol should succeed');
|
|
|
|
/*
|
|
4.4.1.6. Set the RTCSessionSessionDescription
|
|
2.2.6. If description is of type "answer" or "pranswer", then run the
|
|
following steps:
|
|
1. If description initiates the establishment of a new SCTP association,
|
|
as defined in [SCTP-SDP], Sections 10.3 and 10.4, set the value of
|
|
connection's [[sctpTransport]] internal slot to a newly created RTCSctpTransport.
|
|
2. If description negotiates the DTLS role of the SCTP transport, and
|
|
there is an RTCDataChannel with a null id, then generate an ID according
|
|
to [RTCWEB-DATA-PROTOCOL].
|
|
|
|
6.2. createDataChannel
|
|
18. If the [[DataChannelId]] slot is null (due to no ID being passed into
|
|
createDataChannel), and the DTLS role of the SCTP transport has already
|
|
been negotiated, then initialize [[DataChannelId]] to a value generated
|
|
by the user agent, according to [RTCWEB-DATA-PROTOCOL].
|
|
*/
|
|
promise_test(t => {
|
|
const pc = new RTCPeerConnection();
|
|
t.add_cleanup(() => pc.close());
|
|
const channel1 = pc.createDataChannel('channel');
|
|
assert_equals(channel1.id, null,
|
|
'Expect initial id to be null');
|
|
|
|
return pc.createOffer()
|
|
.then(offer =>
|
|
pc.setLocalDescription(offer)
|
|
.then(() => generateAnswer(offer)))
|
|
.then(answer => pc.setRemoteDescription(answer))
|
|
.then(() => {
|
|
assert_not_equals(channel1.id, null,
|
|
'Expect channel1.id to be assigned');
|
|
|
|
assert_greater_than_equal(channel1.id, 0,
|
|
'Expect channel1.id to be set to valid unsigned short');
|
|
|
|
assert_less_than(channel1.id, 65535,
|
|
'Expect channel1.id to be set to valid unsigned short');
|
|
|
|
const channel2 = pc.createDataChannel('channel');
|
|
|
|
assert_not_equals(channel2.id, null,
|
|
'Expect channel2.id to be assigned');
|
|
|
|
assert_greater_than_equal(channel2.id, 0,
|
|
'Expect channel2.id to be set to valid unsigned short');
|
|
|
|
assert_less_than(channel2.id, 65535,
|
|
'Expect channel2.id to be set to valid unsigned short');
|
|
|
|
assert_not_equals(channel2, channel1,
|
|
'Expect channels created from same label to be different');
|
|
|
|
assert_equals(channel2.label, channel1.label,
|
|
'Expect different channnels can have the same label but different id');
|
|
|
|
assert_not_equals(channel2.id, channel1.id,
|
|
'Expect different channnels can have the same label but different id');
|
|
});
|
|
}, 'Channels created after SCTP transport is established should have id assigned');
|
|
|
|
/*
|
|
TODO
|
|
6.1. createDataChannel
|
|
18. If no available ID could be generated, or if the value of the
|
|
id member of the dictionary is taken by an existing RTCDataChannel, throw
|
|
a ResourceInUse exception.
|
|
|
|
Untestable
|
|
6.1. createDataChannel
|
|
16. If a setting, either [[MaxPacketLifeTime]] or [[MaxRetransmits]], has
|
|
been set to indicate unreliable mode, and that value exceeds the maximum
|
|
value supported by the user agent, the value MUST be set to the user
|
|
agents maximum value.
|
|
|
|
20. Create channel's associated underlying data transport and configure
|
|
it according to the relevant properties of channel.
|
|
|
|
Tested in RTCPeerConnection-onnegotiationneeded.html
|
|
21. If channel was the first RTCDataChannel created on connection, update
|
|
the negotiation-needed flag for connection.
|
|
|
|
Issues
|
|
w3c/webrtc-pc#1412
|
|
ResourceInUse exception is not defined
|
|
|
|
Coverage Report
|
|
Tested 22
|
|
Not Tested 1
|
|
Untestable 2
|
|
Total 25
|
|
*/
|
|
</script>
|