mirror of
https://github.com/servo/servo.git
synced 2025-10-04 02:29:12 +01:00
Update web-platform-tests to revision 6e9693d2690e0648fb9a1bd902af7cc078f28515
This commit is contained in:
parent
4ec7dedce1
commit
612038c4d6
56 changed files with 1374 additions and 477 deletions
|
@ -0,0 +1,77 @@
|
|||
<!doctype html>
|
||||
<meta charset="utf-8">
|
||||
<title>RTCCertificate persistent Tests</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/common/get-host-info.sub.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
function findMatchingFingerprint(fingerprints, fingerprint) {
|
||||
for (let f of fingerprints) {
|
||||
if (f.value == fingerprint.value && f.algorithm == fingerprint.algorithm)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function with_iframe(url) {
|
||||
return new Promise(function(resolve) {
|
||||
var frame = document.createElement('iframe');
|
||||
frame.src = url;
|
||||
frame.onload = function() { resolve(frame); };
|
||||
document.body.appendChild(frame);
|
||||
});
|
||||
}
|
||||
|
||||
function testPostMessageCertificate(isCrossOrigin) {
|
||||
promise_test(async t => {
|
||||
let certificate = await RTCPeerConnection.generateCertificate({ name: 'ECDSA', namedCurve: 'P-256' });
|
||||
|
||||
let url = "resources/RTCCertificate-postMessage-iframe.html";
|
||||
if (isCrossOrigin)
|
||||
url = get_host_info().HTTP_REMOTE_ORIGIN + "/webrtc/" + url;
|
||||
|
||||
let iframe = await with_iframe(url);
|
||||
|
||||
let promise = new Promise((resolve, reject) => {
|
||||
window.onmessage = (event) => {
|
||||
resolve(event.data);
|
||||
};
|
||||
t.step_timeout(() => reject("Timed out waiting for frame to send back certificate"), 5000);
|
||||
});
|
||||
iframe.contentWindow.postMessage(certificate, "*");
|
||||
let certificate2 = await promise;
|
||||
|
||||
new RTCPeerConnection({certificates: [certificate]});
|
||||
|
||||
new RTCPeerConnection({certificates: [certificate2]});
|
||||
|
||||
assert_equals(certificate.expires, certificate2.expires);
|
||||
for (let fingerprint of certificate2.getFingerprints())
|
||||
assert_true(findMatchingFingerprint(certificate.getFingerprints(), fingerprint), "check fingerprints");
|
||||
|
||||
iframe.remove();
|
||||
}, "Check " + (isCrossOrigin ? "cross-origin" : "same-origin") + " RTCCertificate serialization");
|
||||
}
|
||||
|
||||
testPostMessageCertificate(false);
|
||||
testPostMessageCertificate(true);
|
||||
|
||||
promise_test(async t => {
|
||||
let url = get_host_info().HTTP_REMOTE_ORIGIN + "/webrtc/resources/RTCCertificate-postMessage-iframe.html";
|
||||
let iframe = await with_iframe(url);
|
||||
|
||||
let promise = new Promise((resolve, reject) => {
|
||||
window.onmessage = (event) => {
|
||||
resolve(event.data);
|
||||
};
|
||||
t.step_timeout(() => reject("Timed out waiting for frame to send back certificate"), 5000);
|
||||
});
|
||||
iframe.contentWindow.postMessage(null, "*");
|
||||
let certificate2 = await promise;
|
||||
|
||||
assert_throws("InvalidAccessError", () => { new RTCPeerConnection({certificates: [certificate2]}) });
|
||||
iframe.remove();
|
||||
}, "Check cross-origin created RTCCertificate");
|
||||
</script>
|
||||
</body>
|
|
@ -6,8 +6,8 @@
|
|||
<script>
|
||||
'use strict';
|
||||
|
||||
// Test is based on the following editor draft:
|
||||
// https://w3c.github.io/webrtc-pc/archives/20170515/webrtc.html
|
||||
// Test is based on the Candidate Recommendation:
|
||||
// https://www.w3.org/TR/webrtc/
|
||||
|
||||
/*
|
||||
4.2.1. RTCConfiguration Dictionary
|
||||
|
@ -26,8 +26,8 @@
|
|||
4.10.2. RTCCertificate Interface
|
||||
interface RTCCertificate {
|
||||
readonly attribute DOMTimeStamp expires;
|
||||
static sequence<AlgorithmIdentifier> getSupportedAlgorithms();
|
||||
sequence<RTCDtlsFingerprint> getFingerprints();
|
||||
AlgorithmIdentifier getAlgorithm();
|
||||
};
|
||||
|
||||
5.5.1 The RTCDtlsFingerprint Dictionary
|
||||
|
@ -257,10 +257,9 @@
|
|||
TODO
|
||||
|
||||
4.10.2. RTCCertificate Interface
|
||||
getAlgorithm
|
||||
Returns the result of the WebCrypto algorithm normalization process
|
||||
[WebCryptoAPI] that occurred when this certificate was generated
|
||||
with generateCertificate().
|
||||
getSupportedAlgorithms
|
||||
Returns a sequence providing a representative set of supported
|
||||
certificate algorithms. At least one algorithm MUST be returned.
|
||||
|
||||
The RTCCertificate object can be stored and retrieved from persistent
|
||||
storage by an application. When a user agent is required to obtain a
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
candidate: '',
|
||||
sdpMid: null,
|
||||
sdpMLineIndex: null,
|
||||
ufrag: undefined
|
||||
usernameFragment: undefined
|
||||
}));
|
||||
}, 'new RTCIceCandidate({ ... }) with manually filled default values');
|
||||
|
||||
|
@ -77,7 +77,7 @@
|
|||
assert_equals(candidate.candidate, '');
|
||||
assert_equals(candidate.sdpMid, 'audio');
|
||||
assert_equals(candidate.sdpMLineIndex, null);
|
||||
assert_equals(candidate.ufrag, null);
|
||||
assert_equals(candidate.usernameFragment, null);
|
||||
}, `new RTCIceCandidate({ sdpMid: 'audio' })`);
|
||||
|
||||
test(t => {
|
||||
|
@ -86,7 +86,7 @@
|
|||
assert_equals(candidate.candidate, '');
|
||||
assert_equals(candidate.sdpMid, null);
|
||||
assert_equals(candidate.sdpMLineIndex, 0);
|
||||
assert_equals(candidate.ufrag, null);
|
||||
assert_equals(candidate.usernameFragment, null);
|
||||
}, 'new RTCIceCandidate({ sdpMLineIndex: 0 })');
|
||||
|
||||
test(t => {
|
||||
|
@ -98,7 +98,7 @@
|
|||
assert_equals(candidate.candidate, '');
|
||||
assert_equals(candidate.sdpMid, 'audio');
|
||||
assert_equals(candidate.sdpMLineIndex, 0);
|
||||
assert_equals(candidate.ufrag, null);
|
||||
assert_equals(candidate.usernameFragment, null);
|
||||
}, `new RTCIceCandidate({ sdpMid: 'audio', sdpMLineIndex: 0 })`);
|
||||
|
||||
test(t => {
|
||||
|
@ -110,7 +110,7 @@
|
|||
assert_equals(candidate.candidate, '');
|
||||
assert_equals(candidate.sdpMid, 'audio');
|
||||
assert_equals(candidate.sdpMLineIndex, null);
|
||||
assert_equals(candidate.ufrag, null);
|
||||
assert_equals(candidate.usernameFragment, null);
|
||||
}, `new RTCIceCandidate({ candidate: '', sdpMid: 'audio' }`);
|
||||
|
||||
test(t => {
|
||||
|
@ -122,7 +122,7 @@
|
|||
assert_equals(candidate.candidate, '');
|
||||
assert_equals(candidate.sdpMid, null);
|
||||
assert_equals(candidate.sdpMLineIndex, 0);
|
||||
assert_equals(candidate.ufrag, null);
|
||||
assert_equals(candidate.usernameFragment, null);
|
||||
}, `new RTCIceCandidate({ candidate: '', sdpMLineIndex: 0 }`);
|
||||
|
||||
test(t => {
|
||||
|
@ -134,7 +134,7 @@
|
|||
assert_equals(candidate.candidate, candidateString);
|
||||
assert_equals(candidate.sdpMid, 'audio');
|
||||
assert_equals(candidate.sdpMLineIndex, null);
|
||||
assert_equals(candidate.ufrag, null);
|
||||
assert_equals(candidate.usernameFragment, null);
|
||||
}, 'new RTCIceCandidate({ ... }) with valid candidate string and sdpMid');
|
||||
|
||||
test(t =>{
|
||||
|
@ -147,7 +147,7 @@
|
|||
assert_equals(candidate.candidate, arbitraryString);
|
||||
assert_equals(candidate.sdpMid, 'audio');
|
||||
assert_equals(candidate.sdpMLineIndex, null);
|
||||
assert_equals(candidate.ufrag, null);
|
||||
assert_equals(candidate.usernameFragment, null);
|
||||
}, 'new RTCIceCandidate({ ... }) with invalid candidate string and sdpMid');
|
||||
|
||||
test(t => {
|
||||
|
@ -155,13 +155,13 @@
|
|||
candidate: candidateString,
|
||||
sdpMid: 'video',
|
||||
sdpMLineIndex: 1,
|
||||
ufrag: 'test'
|
||||
usernameFragment: 'test'
|
||||
});
|
||||
|
||||
assert_equals(candidate.candidate, candidateString);
|
||||
assert_equals(candidate.sdpMid, 'video');
|
||||
assert_equals(candidate.sdpMLineIndex, 1);
|
||||
assert_equals(candidate.ufrag, 'test');
|
||||
assert_equals(candidate.usernameFragment, 'test');
|
||||
}, 'new RTCIceCandidate({ ... }) with non default value for all fields');
|
||||
|
||||
|
||||
|
@ -174,7 +174,7 @@
|
|||
assert_equals(candidate.candidate, '');
|
||||
assert_equals(candidate.sdpMid, arbitraryString);
|
||||
assert_equals(candidate.sdpMLineIndex, null);
|
||||
assert_equals(candidate.ufrag, null);
|
||||
assert_equals(candidate.usernameFragment, null);
|
||||
}, 'new RTCIceCandidate({ ... }) with invalid sdpMid');
|
||||
|
||||
|
||||
|
@ -190,7 +190,7 @@
|
|||
assert_equals(candidate.candidate, '');
|
||||
assert_equals(candidate.sdpMid, null);
|
||||
assert_equals(candidate.sdpMLineIndex, 65535);
|
||||
assert_equals(candidate.ufrag, null);
|
||||
assert_equals(candidate.usernameFragment, null);
|
||||
}, 'new RTCIceCandidate({ ... }) with invalid sdpMLineIndex');
|
||||
|
||||
</script>
|
||||
|
|
|
@ -5,32 +5,6 @@
|
|||
<script>
|
||||
'use strict';
|
||||
|
||||
// Test is based on the following editor draft:
|
||||
// https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.htm
|
||||
|
||||
/*
|
||||
4.3.2. Interface Definition
|
||||
interface RTCPeerConnection : EventTarget {
|
||||
...
|
||||
Promise<void> addIceCandidate((RTCIceCandidateInit or RTCIceCandidate) candidate);
|
||||
};
|
||||
|
||||
interface RTCIceCandidate {
|
||||
readonly attribute DOMString candidate;
|
||||
readonly attribute DOMString? sdpMid;
|
||||
readonly attribute unsigned short? sdpMLineIndex;
|
||||
readonly attribute DOMString? ufrag;
|
||||
...
|
||||
};
|
||||
|
||||
dictionary RTCIceCandidateInit {
|
||||
DOMString candidate = "";
|
||||
DOMString? sdpMid = null;
|
||||
unsigned short? sdpMLineIndex = null;
|
||||
DOMString ufrag;
|
||||
};
|
||||
*/
|
||||
|
||||
// SDP copied from JSEP Example 7.1
|
||||
// It contains two media streams with different ufrags
|
||||
// to test if candidate is added to the correct stream
|
||||
|
@ -89,11 +63,11 @@ a=rtcp-rsize
|
|||
// valid candidate attributes
|
||||
const sdpMid = 'a1';
|
||||
const sdpMLineIndex = 0;
|
||||
const ufrag = 'ETEn';
|
||||
const usernameFragment = 'ETEn';
|
||||
|
||||
const sdpMid2 = 'v1';
|
||||
const sdpMLineIndex2 = 1;
|
||||
const ufrag2 = 'BGKk';
|
||||
const usernameFragment2 = 'BGKk';
|
||||
|
||||
const mediaLine1 = 'm=audio';
|
||||
const mediaLine2 = 'm=video';
|
||||
|
@ -151,7 +125,7 @@ a=rtcp-rsize
|
|||
}, 'Add null candidate should reject with TypeError');
|
||||
|
||||
/*
|
||||
4.3.2. addIceCandidate
|
||||
4.4.2. addIceCandidate
|
||||
4. Return the result of enqueuing the following steps:
|
||||
1. If remoteDescription is null return a promise rejected with a
|
||||
newly created InvalidStateError.
|
||||
|
@ -164,7 +138,7 @@ a=rtcp-rsize
|
|||
return promise_rejects(t, 'InvalidStateError',
|
||||
pc.addIceCandidate({
|
||||
candidate: candidateStr1,
|
||||
sdpMid, sdpMLineIndex, ufrag
|
||||
sdpMid, sdpMLineIndex, usernameFragment
|
||||
}));
|
||||
}, 'Add ICE candidate before setting remote description should reject with InvalidStateError');
|
||||
|
||||
|
@ -179,7 +153,7 @@ a=rtcp-rsize
|
|||
return pc.setRemoteDescription(sessionDesc)
|
||||
.then(() => pc.addIceCandidate({
|
||||
candidate: candidateStr1,
|
||||
sdpMid, sdpMLineIndex, ufrag
|
||||
sdpMid, sdpMLineIndex, usernameFragment
|
||||
}));
|
||||
}, 'Add ICE candidate after setting remote description should succeed');
|
||||
|
||||
|
@ -191,7 +165,7 @@ a=rtcp-rsize
|
|||
return pc.setRemoteDescription(sessionDesc)
|
||||
.then(() => pc.addIceCandidate(new RTCIceCandidate({
|
||||
candidate: candidateStr1,
|
||||
sdpMid, sdpMLineIndex, ufrag
|
||||
sdpMid, sdpMLineIndex, usernameFragment
|
||||
})));
|
||||
}, 'Add ICE candidate with RTCIceCandidate should succeed');
|
||||
|
||||
|
@ -214,12 +188,15 @@ a=rtcp-rsize
|
|||
}, 'Add candidate with only valid sdpMLineIndex should succeed');
|
||||
|
||||
/*
|
||||
4.3.2. addIceCandidate
|
||||
4.4.2. addIceCandidate
|
||||
4.6.2. If candidate is applied successfully, the user agent MUST queue
|
||||
a task that runs the following steps:
|
||||
2. Let remoteDescription be connection's pendingRemoteDescription
|
||||
if not null, otherwise connection's currentRemoteDescription.
|
||||
3. Add candidate to remoteDescription.
|
||||
2. If connection.pendingRemoteDescription is non-null, and represents
|
||||
the ICE generation for which candidate was processed, add
|
||||
candidate to connection.pendingRemoteDescription.
|
||||
3. If connection.currentRemoteDescription is non-null, and represents
|
||||
the ICE generation for which candidate was processed, add
|
||||
candidate to connection.currentRemoteDescription.
|
||||
*/
|
||||
promise_test(t => {
|
||||
const pc = new RTCPeerConnection();
|
||||
|
@ -229,7 +206,7 @@ a=rtcp-rsize
|
|||
return pc.setRemoteDescription(sessionDesc)
|
||||
.then(() => pc.addIceCandidate({
|
||||
candidate: candidateStr1,
|
||||
sdpMid, sdpMLineIndex, ufrag
|
||||
sdpMid, sdpMLineIndex, usernameFragment
|
||||
}))
|
||||
.then(() => {
|
||||
assert_candidate_line_between(pc.remoteDescription.sdp,
|
||||
|
@ -247,7 +224,7 @@ a=rtcp-rsize
|
|||
candidate: candidateStr2,
|
||||
sdpMid: sdpMid2,
|
||||
sdpMLineIndex: sdpMLineIndex2,
|
||||
ufrag: ufrag2
|
||||
usernameFragment: usernameFragment2
|
||||
}))
|
||||
.then(() => {
|
||||
assert_candidate_line_after(pc.remoteDescription.sdp,
|
||||
|
@ -264,13 +241,13 @@ a=rtcp-rsize
|
|||
.then(() => pc.addIceCandidate({
|
||||
candidate: candidateStr1,
|
||||
sdpMid, sdpMLineIndex,
|
||||
ufrag: null
|
||||
usernameFragment: null
|
||||
}))
|
||||
.then(() => {
|
||||
assert_candidate_line_between(pc.remoteDescription.sdp,
|
||||
mediaLine1, candidateLine1, mediaLine2);
|
||||
});
|
||||
}, 'Add candidate for first media stream with null ufrag should add candidate to first media stream');
|
||||
}, 'Add candidate for first media stream with null usernameFragment should add candidate to first media stream');
|
||||
|
||||
promise_test(t => {
|
||||
const pc = new RTCPeerConnection();
|
||||
|
@ -280,13 +257,13 @@ a=rtcp-rsize
|
|||
return pc.setRemoteDescription(sessionDesc)
|
||||
.then(() => pc.addIceCandidate({
|
||||
candidate: candidateStr1,
|
||||
sdpMid, sdpMLineIndex, ufrag
|
||||
sdpMid, sdpMLineIndex, usernameFragment
|
||||
}))
|
||||
.then(() => pc.addIceCandidate({
|
||||
candidate: candidateStr2,
|
||||
sdpMid: sdpMid2,
|
||||
sdpMLineIndex: sdpMLineIndex2,
|
||||
ufrag: ufrag2
|
||||
usernameFragment: usernameFragment2
|
||||
}))
|
||||
.then(() => {
|
||||
assert_candidate_line_between(pc.remoteDescription.sdp,
|
||||
|
@ -298,15 +275,18 @@ a=rtcp-rsize
|
|||
}, 'Adding multiple candidates should add candidates to their corresponding media stream');
|
||||
|
||||
/*
|
||||
4.3.2. addIceCandidate
|
||||
4.4.2. addIceCandidate
|
||||
4.6. If candidate.candidate is an empty string, process candidate as an
|
||||
end-of-candidates indication for the corresponding media description
|
||||
and ICE candidate generation.
|
||||
2. If candidate is applied successfully, the user agent MUST queue
|
||||
a task that runs the following steps:
|
||||
2. Let remoteDescription be connection's pendingRemoteDescription
|
||||
if not null, otherwise connection's currentRemoteDescription.
|
||||
3. Add candidate to remoteDescription.
|
||||
2. If connection.pendingRemoteDescription is non-null, and represents
|
||||
the ICE generation for which candidate was processed, add
|
||||
candidate to connection.pendingRemoteDescription.
|
||||
3. If connection.currentRemoteDescription is non-null, and represents
|
||||
the ICE generation for which candidate was processed, add
|
||||
candidate to connection.currentRemoteDescription.
|
||||
*/
|
||||
promise_test(t => {
|
||||
const pc = new RTCPeerConnection();
|
||||
|
@ -316,12 +296,12 @@ a=rtcp-rsize
|
|||
return pc.setRemoteDescription(sessionDesc)
|
||||
.then(() => pc.addIceCandidate({
|
||||
candidate: candidateStr1,
|
||||
sdpMid, sdpMLineIndex, ufrag
|
||||
sdpMid, sdpMLineIndex, usernameFragment
|
||||
}))
|
||||
.then(() => pc.addIceCandidate({
|
||||
candidate: '',
|
||||
sdpMid, sdpMLineIndex,
|
||||
ufrag
|
||||
usernameFragment
|
||||
}))
|
||||
.then(() => {
|
||||
assert_candidate_line_between(pc.remoteDescription.sdp,
|
||||
|
@ -333,7 +313,7 @@ a=rtcp-rsize
|
|||
}, 'Add with empty candidate string (end of candidate) should succeed');
|
||||
|
||||
/*
|
||||
4.3.2. addIceCandidate
|
||||
4.4.2. addIceCandidate
|
||||
3. If both sdpMid and sdpMLineIndex are null, return a promise rejected
|
||||
with a newly created TypeError.
|
||||
*/
|
||||
|
@ -403,12 +383,12 @@ a=rtcp-rsize
|
|||
candidate: '',
|
||||
sdpMid: null,
|
||||
sdpMLineIndex: null,
|
||||
ufrag: undefined
|
||||
usernameFragment: undefined
|
||||
})));
|
||||
}, 'Add candidate with manually filled default values should reject with TypeError');
|
||||
|
||||
/*
|
||||
4.3.2. addIceCandidate
|
||||
4.4.2. addIceCandidate
|
||||
4.3. If candidate.sdpMid is not null, run the following steps:
|
||||
1. If candidate.sdpMid is not equal to the mid of any media
|
||||
description in remoteDescription , reject p with a newly
|
||||
|
@ -424,12 +404,12 @@ a=rtcp-rsize
|
|||
promise_rejects(t, 'OperationError',
|
||||
pc.addIceCandidate({
|
||||
candidate: candidateStr1,
|
||||
sdpMid: 'invalid', sdpMLineIndex, ufrag
|
||||
sdpMid: 'invalid', sdpMLineIndex, usernameFragment
|
||||
})));
|
||||
}, 'Add candidate with invalid sdpMid should reject with OperationError');
|
||||
|
||||
/*
|
||||
4.3.2. addIceCandidate
|
||||
4.4.2. addIceCandidate
|
||||
4.4. Else, if candidate.sdpMLineIndex is not null, run the following
|
||||
steps:
|
||||
1. If candidate.sdpMLineIndex is equal to or larger than the
|
||||
|
@ -447,7 +427,7 @@ a=rtcp-rsize
|
|||
pc.addIceCandidate({
|
||||
candidate: candidateStr1,
|
||||
sdpMLineIndex: 2,
|
||||
ufrag
|
||||
usernameFragment
|
||||
})));
|
||||
}, 'Add candidate with invalid sdpMLineIndex should reject with OperationError');
|
||||
|
||||
|
@ -463,7 +443,7 @@ a=rtcp-rsize
|
|||
candidate: candidateStr1,
|
||||
sdpMid,
|
||||
sdpMLineIndex: 2,
|
||||
ufrag
|
||||
usernameFragment
|
||||
}));
|
||||
}, 'Invalid sdpMLineIndex should be ignored if valid sdpMid is provided');
|
||||
|
||||
|
@ -477,18 +457,18 @@ a=rtcp-rsize
|
|||
candidate: candidateStr2,
|
||||
sdpMid: sdpMid2,
|
||||
sdpMLineIndex: sdpMLineIndex2,
|
||||
ufrag: null
|
||||
usernameFragment: null
|
||||
}))
|
||||
.then(() => {
|
||||
assert_candidate_line_after(pc.remoteDescription.sdp,
|
||||
mediaLine2, candidateLine2);
|
||||
});
|
||||
}, 'Add candidate for media stream 2 with null ufrag should succeed');
|
||||
}, 'Add candidate for media stream 2 with null usernameFragment should succeed');
|
||||
|
||||
/*
|
||||
4.3.2. addIceCandidate
|
||||
4.5. If candidate.ufrag is neither undefined nor null, and is not equal
|
||||
to any ufrag present in the corresponding media description of an
|
||||
4.5. If candidate.usernameFragment is neither undefined nor null, and is not equal
|
||||
to any usernameFragment present in the corresponding media description of an
|
||||
applied remote description, reject p with a newly created
|
||||
OperationError and abort these steps.
|
||||
*/
|
||||
|
@ -503,12 +483,12 @@ a=rtcp-rsize
|
|||
pc.addIceCandidate({
|
||||
candidate: candidateStr1,
|
||||
sdpMid, sdpMLineIndex,
|
||||
ufrag: 'invalid'
|
||||
usernameFragment: 'invalid'
|
||||
})));
|
||||
}, 'Add candidate with invalid ufrag should reject with OperationError');
|
||||
}, 'Add candidate with invalid usernameFragment should reject with OperationError');
|
||||
|
||||
/*
|
||||
4.3.2. addIceCandidate
|
||||
4.4.2. addIceCandidate
|
||||
4.6.1. If candidate could not be successfully added the user agent MUST
|
||||
queue a task that runs the following steps:
|
||||
2. Reject p with a DOMException object whose name attribute has
|
||||
|
@ -524,7 +504,7 @@ a=rtcp-rsize
|
|||
promise_rejects(t, 'OperationError',
|
||||
pc.addIceCandidate({
|
||||
candidate: invalidCandidateStr,
|
||||
sdpMid, sdpMLineIndex, ufrag
|
||||
sdpMid, sdpMLineIndex, usernameFragment
|
||||
})));
|
||||
}, 'Add candidate with invalid candidate string should reject with OperationError');
|
||||
|
||||
|
@ -540,52 +520,8 @@ a=rtcp-rsize
|
|||
candidate: candidateStr2,
|
||||
sdpMid: sdpMid2,
|
||||
sdpMLineIndex: sdpMLineIndex2,
|
||||
ufrag: ufrag
|
||||
usernameFragment
|
||||
})));
|
||||
}, 'Add candidate with sdpMid belonging to different ufrag should reject with OperationError');
|
||||
}, 'Add candidate with sdpMid belonging to different usernameFragment should reject with OperationError');
|
||||
|
||||
/*
|
||||
TODO
|
||||
4.3.2. addIceCandidate
|
||||
4.6. In parallel, add the ICE candidate candidate as described in [JSEP]
|
||||
(section 4.1.17.). Use candidate.ufrag to identify the ICE generation;
|
||||
|
||||
If the ufrag is null, process the candidate for the most recent ICE
|
||||
generation.
|
||||
|
||||
- Call with candidate string containing partial malformed syntax, i.e. malformed IP.
|
||||
Some browsers may ignore the syntax error and add it to the SDP regardless.
|
||||
|
||||
Non-Testable
|
||||
4.3.2. addIceCandidate
|
||||
4.6. (The steps are non-testable because the abort step in enqueue operation
|
||||
steps in before they can reach here):
|
||||
1. If candidate could not be successfully added the user agent MUST
|
||||
queue a task that runs the following steps:
|
||||
1. If connection's [[isClosed]] slot is true, then abort
|
||||
these steps.
|
||||
|
||||
2. If candidate is applied successfully, the user agent MUST queue
|
||||
a task that runs the following steps:
|
||||
1. If connection's [[isClosed]] slot is true, then abort these steps.
|
||||
|
||||
Issues
|
||||
w3c/webrtc-pc#1213
|
||||
addIceCandidate end of candidates woes
|
||||
|
||||
w3c/webrtc-pc#1216
|
||||
Clarify addIceCandidate behavior when adding candidate after end of candidate
|
||||
|
||||
w3c/webrtc-pc#1227
|
||||
addIceCandidate may add ice candidate to the wrong remote description
|
||||
|
||||
w3c/webrtc-pc#1345
|
||||
Make promise rejection/enqueing consistent
|
||||
|
||||
Coverage Report
|
||||
Total: 23
|
||||
Tested: 19
|
||||
Not Tested: 2
|
||||
Non-Testable: 2
|
||||
*/
|
||||
</script>
|
||||
|
|
|
@ -1,400 +0,0 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<title>RTCPeerConnection.prototype.getIdentityAssertion</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="identity-helper.sub.js"></script>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
// Test is based on the following editor draft:
|
||||
// https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.html
|
||||
|
||||
// The tests here interacts with the mock identity provider located at
|
||||
// /.well-known/idp-proxy/mock-idp.js
|
||||
|
||||
// The following helper functions are called from identity-helper.sub.js
|
||||
// parseAssertionResult
|
||||
// getIdpDomains
|
||||
// assert_rtcerror_rejection
|
||||
// hostString
|
||||
|
||||
/*
|
||||
9.6. RTCPeerConnection Interface Extensions
|
||||
partial interface RTCPeerConnection {
|
||||
void setIdentityProvider(DOMString provider,
|
||||
optional RTCIdentityProviderOptions options);
|
||||
Promise<DOMString> getIdentityAssertion();
|
||||
readonly attribute Promise<RTCIdentityAssertion> peerIdentity;
|
||||
readonly attribute DOMString? idpLoginUrl;
|
||||
readonly attribute DOMString? idpErrorInfo;
|
||||
};
|
||||
|
||||
dictionary RTCIdentityProviderOptions {
|
||||
DOMString protocol = "default";
|
||||
DOMString usernameHint;
|
||||
DOMString peerIdentity;
|
||||
};
|
||||
*/
|
||||
promise_test(t => {
|
||||
const pc = new RTCPeerConnection();
|
||||
const port = window.location.port;
|
||||
|
||||
const [idpDomain] = getIdpDomains();
|
||||
const idpHost = hostString(idpDomain, port);
|
||||
|
||||
pc.setIdentityProvider(idpHost, {
|
||||
protocol: 'mock-idp.js?foo=bar',
|
||||
usernameHint: `alice@${idpDomain}`,
|
||||
peerIdentity: 'bob@example.org'
|
||||
});
|
||||
|
||||
return pc.getIdentityAssertion()
|
||||
.then(assertionResultStr => {
|
||||
const { idp, assertion } = parseAssertionResult(assertionResultStr);
|
||||
|
||||
assert_equals(idp.domain, idpHost,
|
||||
'Expect mock-idp.js to construct domain from its location.host');
|
||||
|
||||
assert_equals(idp.protocol, 'mock-idp.js',
|
||||
'Expect mock-idp.js to return protocol of itself with no query string');
|
||||
|
||||
const {
|
||||
watermark,
|
||||
args,
|
||||
env,
|
||||
query,
|
||||
} = assertion;
|
||||
|
||||
assert_equals(watermark, 'mock-idp.js.watermark',
|
||||
'Expect assertion result to contain watermark left by mock-idp.js');
|
||||
|
||||
assert_equals(args.origin, window.origin,
|
||||
'Expect args.origin argument to be the origin of this window');
|
||||
|
||||
assert_equals(env.location,
|
||||
`https://${idpHost}/.well-known/idp-proxy/idp-test.js?foo=bar`,
|
||||
'Expect IdP proxy to be loaded with full well-known URL constructed from provider and protocol');
|
||||
|
||||
assert_equals(env.origin, `https://${idpHost}`,
|
||||
'Expect IdP to have its own origin');
|
||||
|
||||
assert_equals(args.options.protocol, 'idp-test.js?foo=bar',
|
||||
'Expect options.protocol to be the same value as being passed from here');
|
||||
|
||||
assert_equals(args.options.usernameHint, `alice@${idpDomain}`,
|
||||
'Expect options.usernameHint to be the same value as being passed from here');
|
||||
|
||||
assert_equals(args.options.peerIdentity, 'bob@example.org',
|
||||
'Expect options.peerIdentity to be the same value as being passed from here');
|
||||
|
||||
assert_equals(query.foo, 'bar',
|
||||
'Expect query string to be parsed by mock-idp.js and returned back');
|
||||
});
|
||||
}, 'getIdentityAssertion() should load IdP proxy and return assertion generated');
|
||||
|
||||
// When generating assertion, the RTCPeerConnection doesn't care if the returned assertion
|
||||
// represents identity of different domain
|
||||
promise_test(t => {
|
||||
const pc = new RTCPeerConnection();
|
||||
const port = window.location.port;
|
||||
|
||||
const [idpDomain1, idpDomain2] = getIdpDomains();
|
||||
assert_not_equals(idpDomain1, idpDomain2,
|
||||
'Sanity check two idpDomains are different');
|
||||
|
||||
// Ask mock-idp.js to return a custom domain idpDomain2 and custom protocol foo
|
||||
pc.setIdentityProvider(hostString(idpDomain1, port), {
|
||||
protocol: `mock-idp.js?generatorAction=return-custom-idp&domain=${idpDomain2}&protocol=foo`,
|
||||
usernameHint: `alice@${idpDomain2}`,
|
||||
});
|
||||
|
||||
return pc.getIdentityAssertion()
|
||||
.then(assertionResultStr => {
|
||||
const { idp, assertion } = parseAssertionResult(assertionResultStr);
|
||||
assert_equals(idp.domain, idpDomain2);
|
||||
assert_equals(idp.protocol, 'foo');
|
||||
assert_equals(assertion.options.usernameHint, `alice@${idpDomain2}`);
|
||||
});
|
||||
}, 'getIdentityAssertion() should succeed if mock-idp.js return different domain and protocol in assertion');
|
||||
|
||||
/*
|
||||
9.3. Requesting Identity Assertions
|
||||
4. If the IdP proxy produces an error or returns a promise that does not resolve to
|
||||
a valid RTCIdentityValidationResult (see 9.5 IdP Error Handling), then identity
|
||||
validation fails.
|
||||
|
||||
9.5. IdP Error Handling
|
||||
- If an identity provider throws an exception or returns a promise that is ultimately
|
||||
rejected, then the procedure that depends on the IdP MUST also fail. These types of
|
||||
errors will cause an IdP failure with an RTCError with errorDetail set to
|
||||
"idp-execution-failure".
|
||||
|
||||
9.6. RTCPeerConnection Interface Extensions
|
||||
idpErrorInfo
|
||||
An attribute that the IdP can use to pass additional information back to the
|
||||
applications about the error. The format of this string is defined by the IdP and
|
||||
may be JSON.
|
||||
*/
|
||||
promise_test(t => {
|
||||
const pc = new RTCPeerConnection();
|
||||
|
||||
assert_equals(pc.idpErrorInfo, null,
|
||||
'Expect initial pc.idpErrorInfo to be null');
|
||||
|
||||
const port = window.location.port;
|
||||
const [idpDomain] = getIdpDomains();
|
||||
|
||||
// Ask mock-idp.js to throw an error with err.errorInfo set to bar
|
||||
pc.setIdentityProvider(hostString(idpDomain, port), {
|
||||
protocol: `mock-idp.js?generatorAction=throw-error&errorInfo=bar`,
|
||||
usernameHint: `alice@${idpDomain}`,
|
||||
});
|
||||
|
||||
return assert_rtcerror_rejection('idp-execution-failure',
|
||||
pc.getIdentityAssertion())
|
||||
.then(() => {
|
||||
assert_equals(pc.idpErrorInfo, 'bar',
|
||||
'Expect pc.idpErrorInfo to be set to the err.idpErrorInfo thrown by mock-idp.js');
|
||||
});
|
||||
}, `getIdentityAssertion() should reject with RTCError('idp-execution-failure') if mock-idp.js throws error`);
|
||||
|
||||
/*
|
||||
9.5. IdP Error Handling
|
||||
- If the script loaded from the identity provider is not valid JavaScript or does
|
||||
not implement the correct interfaces, it causes an IdP failure with an RTCError
|
||||
with errorDetail set to "idp-bad-script-failure".
|
||||
*/
|
||||
promise_test(t => {
|
||||
const pc = new RTCPeerConnection();
|
||||
|
||||
const port = window.location.port;
|
||||
const [idpDomain] = getIdpDomains();
|
||||
|
||||
// Ask mock-idp.js to not register its callback to the
|
||||
// RTCIdentityProviderRegistrar
|
||||
pc.setIdentityProvider(hostString(idpDomain, port), {
|
||||
protocol: `mock-idp.js?action=do-not-register`,
|
||||
usernameHint: `alice@${idpDomain}`,
|
||||
});
|
||||
|
||||
return assert_rtcerror_rejection('idp-bad-script-failure',
|
||||
pc.getIdentityAssertion());
|
||||
|
||||
}, `getIdentityAssertion() should reject with RTCError('idp-bad-script-failure') if IdP proxy script do not register its callback`);
|
||||
|
||||
/*
|
||||
9.3. Requesting Identity Assertions
|
||||
4. If the IdP proxy produces an error or returns a promise that does not resolve
|
||||
to a valid RTCIdentityAssertionResult (see 9.5 IdP Error Handling), then assertion
|
||||
generation fails.
|
||||
*/
|
||||
promise_test(t => {
|
||||
const pc = new RTCPeerConnection();
|
||||
|
||||
const port = window.location.port;
|
||||
const [idpDomain] = getIdpDomains();
|
||||
|
||||
// Ask mock-idp.js to return an invalid result that is not proper
|
||||
// RTCIdentityAssertionResult
|
||||
pc.setIdentityProvider(hostString(idpDomain, port), {
|
||||
protocol: `mock-idp.js?generatorAction=return-invalid-result`,
|
||||
usernameHint: `alice@${idpDomain}`,
|
||||
});
|
||||
|
||||
return promise_rejects(t, 'OperationError',
|
||||
pc.getIdentityAssertion());
|
||||
}, `getIdentityAssertion() should reject with OperationError if mock-idp.js return invalid result`);
|
||||
|
||||
/*
|
||||
9.5. IdP Error Handling
|
||||
- A RTCPeerConnection might be configured with an identity provider, but loading of
|
||||
the IdP URI fails. Any procedure that attempts to invoke such an identity provider
|
||||
and cannot load the URI fails with an RTCError with errorDetail set to
|
||||
"idp-load-failure" and the httpRequestStatusCode attribute of the error set to the
|
||||
HTTP status code of the response.
|
||||
*/
|
||||
promise_test(t => {
|
||||
const pc = new RTCPeerConnection();
|
||||
|
||||
pc.setIdentityProvider('nonexistent.{{domains[]}}', {
|
||||
protocol: `non-existent`,
|
||||
usernameHint: `alice@example.org`,
|
||||
});
|
||||
|
||||
return assert_rtcerror_rejection('idp-load-failure',
|
||||
pc.getIdentityAssertion());
|
||||
}, `getIdentityAssertion() should reject with RTCError('idp-load-failure') if IdP cannot be loaded`);
|
||||
|
||||
/*
|
||||
9.3.1. User Login Procedure
|
||||
Rejecting the promise returned by generateAssertion will cause the error to
|
||||
propagate to the application. Login errors are indicated by rejecting the
|
||||
promise with an RTCError with errorDetail set to "idp-need-login".
|
||||
|
||||
The URL to login at will be passed to the application in the idpLoginUrl
|
||||
attribute of the RTCPeerConnection.
|
||||
|
||||
9.5. IdP Error Handling
|
||||
- If the identity provider requires the user to login, the operation will fail
|
||||
RTCError with errorDetail set to "idp-need-login" and the idpLoginUrl attribute
|
||||
of the error set to the URL that can be used to login.
|
||||
*/
|
||||
promise_test(t => {
|
||||
const pc = new RTCPeerConnection();
|
||||
|
||||
assert_equals(pc.idpLoginUrl, null,
|
||||
'Expect initial pc.idpLoginUrl to be null');
|
||||
|
||||
const port = window.location.port;
|
||||
const [idpDomain] = getIdpDomains();
|
||||
const idpHost = hostString(idpDomain, port);
|
||||
|
||||
pc.setIdentityProvider(idpHost, {
|
||||
protocol: `mock-idp.js?generatorAction=require-login`,
|
||||
usernameHint: `alice@${idpDomain}`,
|
||||
});
|
||||
|
||||
return assert_rtcerror_rejection('idp-need-login',
|
||||
pc.getIdentityAssertion())
|
||||
.then(err => {
|
||||
assert_equals(err.idpLoginUrl, `https://${idpHost}/login`,
|
||||
'Expect err.idpLoginUrl to be set to url set by mock-idp.js');
|
||||
|
||||
assert_equals(pc.idpLoginUrl, `https://${idpHost}/login`,
|
||||
'Expect pc.idpLoginUrl to be set to url set by mock-idp.js');
|
||||
|
||||
assert_equals(pc.idpErrorInfo, 'login required',
|
||||
'Expect pc.idpErrorInfo to be set to info set by mock-idp.js');
|
||||
});
|
||||
}, `getIdentityAssertion() should reject with RTCError('idp-need-login') when mock-idp.js requires login`);
|
||||
|
||||
/*
|
||||
RTCIdentityProviderOptions Members
|
||||
peerIdentity
|
||||
The identity of the peer. For identity providers that bind their assertions to a
|
||||
particular pair of communication peers, this allows them to generate an assertion
|
||||
that includes both local and remote identities. If this value is omitted, but a
|
||||
value is provided for the peerIdentity member of RTCConfiguration, the value from
|
||||
RTCConfiguration is used.
|
||||
*/
|
||||
promise_test(t => {
|
||||
const pc = new RTCPeerConnection({
|
||||
peerIdentity: 'bob@example.net'
|
||||
});
|
||||
|
||||
const port = window.location.port;
|
||||
const [idpDomain] = getIdpDomains();
|
||||
const idpHost = hostString(idpDomain, port);
|
||||
|
||||
pc.setIdentityProvider(idpHost, {
|
||||
protocol: 'mock-idp.js'
|
||||
});
|
||||
|
||||
return pc.getIdentityAssertion()
|
||||
.then(assertionResultStr => {
|
||||
const { assertion } = parseAssertionResult(assertionResultStr);
|
||||
assert_equals(assertion.args.options.peerIdentity, 'bob@example.net');
|
||||
});
|
||||
}, 'setIdentityProvider() with no peerIdentity provided should use peerIdentity value from getConfiguration()');
|
||||
|
||||
/*
|
||||
9.6. setIdentityProvider
|
||||
3. If any identity provider value has changed, discard any stored identity assertion.
|
||||
*/
|
||||
promise_test(t => {
|
||||
const pc = new RTCPeerConnection();
|
||||
const port = window.location.port;
|
||||
const [idpDomain] = getIdpDomains();
|
||||
const idpHost = hostString(idpDomain, port);
|
||||
|
||||
pc.setIdentityProvider(idpHost, {
|
||||
protocol: 'mock-idp.js?mark=first'
|
||||
});
|
||||
|
||||
return pc.getIdentityAssertion()
|
||||
.then(assertionResultStr => {
|
||||
const { assertion } = parseAssertionResult(assertionResultStr);
|
||||
assert_equals(assertion.query.mark, 'first');
|
||||
|
||||
pc.setIdentityProvider(idpHost, {
|
||||
protocol: 'mock-idp.js?mark=second'
|
||||
});
|
||||
|
||||
return pc.getIdentityAssertion();
|
||||
})
|
||||
.then(assertionResultStr => {
|
||||
const { assertion } = parseAssertionResult(assertionResultStr);
|
||||
assert_equals(assertion.query.mark, 'second',
|
||||
'Expect generated assertion is from second IdP config');
|
||||
});
|
||||
}, `Calling setIdentityProvider() multiple times should reset identity assertions`);
|
||||
|
||||
promise_test(t => {
|
||||
const pc = new RTCPeerConnection();
|
||||
const port = window.location.port;
|
||||
const [idpDomain] = getIdpDomains();
|
||||
|
||||
pc.setIdentityProvider(hostString(idpDomain, port), {
|
||||
protocol: 'mock-idp.js',
|
||||
usernameHint: `alice@${idpDomain}`
|
||||
});
|
||||
|
||||
return pc.getIdentityAssertion()
|
||||
.then(assertionResultStr =>
|
||||
pc.createOffer()
|
||||
.then(offer => {
|
||||
assert_true(offer.sdp.includes(`\r\na=identity:${assertionResultStr}`,
|
||||
'Expect SDP to have a=identity line containing assertion string'));
|
||||
}));
|
||||
}, 'createOffer() should return SDP containing identity assertion string if identity provider is set');
|
||||
|
||||
/*
|
||||
4.4.2. Steps to create an offer
|
||||
1. If the need for an identity assertion was identified when createOffer was
|
||||
invoked, wait for the identity assertion request process to complete.
|
||||
2. If the identity provider was unable to produce an identity assertion, reject p
|
||||
with a newly created NotReadableError and abort these steps.
|
||||
*/
|
||||
promise_test(t => {
|
||||
const pc = new RTCPeerConnection();
|
||||
const port = window.location.port;
|
||||
const [idpDomain] = getIdpDomains();
|
||||
|
||||
pc.setIdentityProvider(hostString(idpDomain, port), {
|
||||
protocol: 'mock-idp.js?generatorAction=throw-error',
|
||||
usernameHint: `alice@${idpDomain}`
|
||||
});
|
||||
|
||||
return promise_rejects(t, 'NotReadableError',
|
||||
pc.createOffer());
|
||||
}, 'createOffer() should reject with NotReadableError if identitity assertion request fails');
|
||||
|
||||
/*
|
||||
4.4.2. Steps to create an answer
|
||||
1. If the need for an identity assertion was identified when createAnswer was
|
||||
invoked, wait for the identity assertion request process to complete.
|
||||
|
||||
2. If the identity provider was unable to produce an identity assertion, reject p
|
||||
with a newly created NotReadableError and abort these steps.
|
||||
*/
|
||||
promise_test(t => {
|
||||
const pc = new RTCPeerConnection();
|
||||
const port = window.location.port;
|
||||
const [idpDomain] = getIdpDomains();
|
||||
|
||||
pc.setIdentityProvider(hostString(idpDomain, port), {
|
||||
protocol: 'mock-idp.js?generatorAction=throw-error',
|
||||
usernameHint: `alice@${idpDomain}`
|
||||
});
|
||||
|
||||
return new RTCPeerConnection()
|
||||
.createOffer()
|
||||
.then(offer => pc.setRemoteDescription(offer))
|
||||
.then(() =>
|
||||
promise_rejects(t, 'NotReadableError',
|
||||
pc.createAnswer()));
|
||||
|
||||
}, 'createAnswer() should reject with NotReadableError if identitity assertion request fails');
|
||||
|
||||
</script>
|
|
@ -358,7 +358,7 @@ const trackFactories = {
|
|||
*/
|
||||
canCreate(requested) {
|
||||
const supported = {
|
||||
audio: !!window.MediaStreamAudioDestinationNode,
|
||||
audio: !!window.AudioContext && !!window.MediaStreamAudioDestinationNode,
|
||||
video: !!HTMLCanvasElement.prototype.captureStream
|
||||
};
|
||||
|
||||
|
|
|
@ -1,336 +0,0 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<title>RTCPeerConnection.prototype.peerIdentity</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="identity-helper.sub.js"></script>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
// Test is based on the following editor draft:
|
||||
// https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.html
|
||||
|
||||
// The tests here interacts with the mock identity provider located at
|
||||
// /.well-known/idp-proxy/mock-idp.js
|
||||
|
||||
// The following helper functions are called from identity-helper.sub.js
|
||||
// parseAssertionResult
|
||||
// getIdpDomains
|
||||
// assert_rtcerror_rejection
|
||||
// hostString
|
||||
|
||||
/*
|
||||
9.6. RTCPeerConnection Interface Extensions
|
||||
partial interface RTCPeerConnection {
|
||||
void setIdentityProvider(DOMString provider,
|
||||
optional RTCIdentityProviderOptions options);
|
||||
Promise<DOMString> getIdentityAssertion();
|
||||
readonly attribute Promise<RTCIdentityAssertion> peerIdentity;
|
||||
readonly attribute DOMString? idpLoginUrl;
|
||||
readonly attribute DOMString? idpErrorInfo;
|
||||
};
|
||||
|
||||
dictionary RTCIdentityProviderOptions {
|
||||
DOMString protocol = "default";
|
||||
DOMString usernameHint;
|
||||
DOMString peerIdentity;
|
||||
};
|
||||
|
||||
[Constructor(DOMString idp, DOMString name)]
|
||||
interface RTCIdentityAssertion {
|
||||
attribute DOMString idp;
|
||||
attribute DOMString name;
|
||||
};
|
||||
*/
|
||||
|
||||
/*
|
||||
4.3.2. setRemoteDescription
|
||||
If an a=identity attribute is present in the session description, the browser
|
||||
validates the identity assertion..
|
||||
|
||||
If the "peerIdentity" configuration is applied to the RTCPeerConnection, this
|
||||
establishes a target peer identity of the provided value. Alternatively, if the
|
||||
RTCPeerConnection has previously authenticated the identity of the peer (that
|
||||
is, there is a current value for peerIdentity ), then this also establishes a
|
||||
target peer identity.
|
||||
*/
|
||||
promise_test(t => {
|
||||
const pc1 = new RTCPeerConnection();
|
||||
t.add_cleanup(() => pc1.close());
|
||||
const pc2 = new RTCPeerConnection();
|
||||
|
||||
t.add_cleanup(() => pc2.close());
|
||||
|
||||
const port = window.location.port;
|
||||
const [idpDomain] = getIdpDomains();
|
||||
const idpHost = hostString(idpDomain, port);
|
||||
|
||||
pc1.setIdentityProvider(idpHost, {
|
||||
protocol: 'mock-idp.js',
|
||||
usernameHint: `alice@${idpDomain}`
|
||||
});
|
||||
|
||||
return pc1.createOffer()
|
||||
.then(offer => pc2.setRemoteDescription(offer))
|
||||
.then(() => pc2.peerIdentity)
|
||||
.then(identityAssertion => {
|
||||
const { idp, name } = identityAssertion;
|
||||
assert_equals(idp, idpDomain, `Expect IdP domain to be ${idpDomain}`);
|
||||
assert_equals(identityAssertion, `alice@${idpDomain}`,
|
||||
`Expect validated identity from mock-idp.js to be same as specified in usernameHint`);
|
||||
});
|
||||
}, 'setRemoteDescription() on offer with a=identity should establish peerIdentity');
|
||||
|
||||
/*
|
||||
4.3.2. setRemoteDescription
|
||||
The target peer identity cannot be changed once set. Once set, if a different
|
||||
value is provided, the user agent MUST reject the returned promise with a newly
|
||||
created InvalidModificationError and abort this operation. The RTCPeerConnection
|
||||
MUST be closed if the validated peer identity does not match the target peer
|
||||
identity.
|
||||
*/
|
||||
promise_test(t => {
|
||||
const port = window.location.port;
|
||||
const [idpDomain] = getIdpDomains();
|
||||
const idpHost = hostString(idpDomain, port);
|
||||
|
||||
const pc1 = new RTCPeerConnection();
|
||||
t.add_cleanup(() => pc1.close());
|
||||
const pc2 = new RTCPeerConnection({
|
||||
peerIdentity: `bob@${idpDomain}`
|
||||
});
|
||||
|
||||
t.add_cleanup(() => pc2.close());
|
||||
|
||||
pc1.setIdentityProvider(idpHost, {
|
||||
protocol: 'mock-idp.js',
|
||||
usernameHint: `alice@${idpDomain}`
|
||||
});
|
||||
|
||||
return pc1.createOffer()
|
||||
.then(offer =>
|
||||
promise_rejects(t, 'InvalidModificationError',
|
||||
pc2.setRemoteDescription(offer)))
|
||||
.then(() => {
|
||||
assert_true(pc2.signalingState, 'closed',
|
||||
'Expect peer connection to be closed after mismatch peer identity');
|
||||
});
|
||||
}, 'setRemoteDescription() on offer with a=identity that resolve to value different from target peer identity should reject with InvalidModificationError');
|
||||
|
||||
/*
|
||||
9.4. Verifying Identity Assertions
|
||||
8. The RTCPeerConnection decodes the contents and validates that it contains a
|
||||
fingerprint value for every a=fingerprint attribute in the session description.
|
||||
This ensures that the certificate used by the remote peer for communications
|
||||
is covered by the identity assertion.
|
||||
|
||||
If identity validation fails, the peerIdentity promise is rejected with a newly
|
||||
created OperationError.
|
||||
|
||||
If identity validation fails and there is a target peer identity for the
|
||||
RTCPeerConnection, the promise returned by setRemoteDescription MUST be rejected
|
||||
with the same DOMException.
|
||||
*/
|
||||
promise_test(t => {
|
||||
const port = window.location.port;
|
||||
const [idpDomain] = getIdpDomains();
|
||||
const idpHost = hostString(idpDomain, port);
|
||||
|
||||
const pc1 = new RTCPeerConnection();
|
||||
t.add_cleanup(() => pc1.close());
|
||||
const pc2 = new RTCPeerConnection({
|
||||
peerIdentity: `alice@${idpDomain}`
|
||||
});
|
||||
|
||||
t.add_cleanup(() => pc2.close());
|
||||
|
||||
// Ask mockidp.js to return custom contents in validation result
|
||||
pc1.setIdentityProvider(idpHost, {
|
||||
protocol: 'mock-idp.js?validatorAction=return-custom-contents&contents=bogus',
|
||||
usernameHint: `alice@${idpDomain}`
|
||||
});
|
||||
|
||||
const peerIdentityPromise = pc2.peerIdentity;
|
||||
|
||||
return pc1.createOffer()
|
||||
.then(offer => Promise.all([
|
||||
promise_rejects(t, 'OperationError',
|
||||
pc2.setRemoteDescription(offer)),
|
||||
promise_rejects(t, 'OperationError',
|
||||
peerIdentityPromise)
|
||||
]));
|
||||
}, 'setRemoteDescription() with peerIdentity set and with IdP proxy that return validationAssertion with mismatch contents should reject with OperationError');
|
||||
|
||||
/*
|
||||
9.4. Verifying Identity Assertions
|
||||
9. The RTCPeerConnection validates that the domain portion of the identity matches
|
||||
the domain of the IdP as described in [RTCWEB-SECURITY-ARCH]. If this check
|
||||
fails then the identity validation fails.
|
||||
*/
|
||||
promise_test(t => {
|
||||
const port = window.location.port;
|
||||
const [idpDomain1, idpDomain2] = getIdpDomains();
|
||||
assert_not_equals(idpDomain1, idpDomain2,
|
||||
'Sanity check two idpDomains are different');
|
||||
|
||||
const idpHost1 = hostString(idpDomain1, port);
|
||||
|
||||
const pc1 = new RTCPeerConnection();
|
||||
t.add_cleanup(() => pc1.close());
|
||||
const pc2 = new RTCPeerConnection({
|
||||
peerIdentity: `alice@${idpDomain2}`
|
||||
});
|
||||
|
||||
t.add_cleanup(() => pc2.close());
|
||||
|
||||
// mock-idp.js will return assertion of domain2 identity
|
||||
// with domain1 in the idp.domain field
|
||||
pc1.setIdentityProvider(idpHost1, {
|
||||
protocol: 'mock-idp.js',
|
||||
usernameHint: `alice@${idpDomain2}`
|
||||
});
|
||||
|
||||
return pc1.getIdentityAssertion()
|
||||
.then(assertionResultStr => {
|
||||
const { idp, assertion } = parseAssertionResult(assertionResultStr);
|
||||
|
||||
assert_equals(idp.domain, idpDomain1,
|
||||
'Sanity check domain of assertion is domain1');
|
||||
|
||||
assert_equals(assertion.options.usernameHint, `alice@${idpDomain2}`,
|
||||
'Sanity check domain1 is going to validate a domain2 identity');
|
||||
|
||||
return pc1.createOffer();
|
||||
})
|
||||
.then(offer => Promise.all([
|
||||
promise_rejects(t, 'OperationError',
|
||||
pc2.setRemoteDescription(offer)),
|
||||
promise_rejects(t, 'OperationError',
|
||||
pc2.peerIdentity)
|
||||
]));
|
||||
}, 'setRemoteDescription() and peerIdentity should reject with OperationError if IdP return validated identity that is different from its own domain');
|
||||
|
||||
/*
|
||||
9.4 Verifying Identity Assertions
|
||||
If identity validation fails and there is a target peer identity for the
|
||||
RTCPeerConnection, the promise returned by setRemoteDescription MUST be rejected
|
||||
with the same DOMException.
|
||||
|
||||
9.5 IdP Error Handling
|
||||
- If an identity provider throws an exception or returns a promise that is ultimately
|
||||
rejected, then the procedure that depends on the IdP MUST also fail. These types of
|
||||
errors will cause an IdP failure with an RTCError with errorDetail set to
|
||||
"idp-execution-failure".
|
||||
|
||||
Any error generated by the IdP MAY provide additional information in the
|
||||
idpErrorInfo attribute. The information in this string is defined by the
|
||||
IdP in use.
|
||||
*/
|
||||
promise_test(t => {
|
||||
const port = window.location.port;
|
||||
const [idpDomain] = getIdpDomains();
|
||||
const idpHost = hostString(idpDomain, port);
|
||||
|
||||
const pc1 = new RTCPeerConnection();
|
||||
t.add_cleanup(() => pc1.close());
|
||||
const pc2 = new RTCPeerConnection({
|
||||
peerIdentity: `alice@${idpDomain}`
|
||||
});
|
||||
|
||||
t.add_cleanup(() => pc2.close());
|
||||
|
||||
// Ask mock-idp.js to throw error during validation,
|
||||
// i.e. during pc2.setRemoteDescription()
|
||||
pc1.setIdentityProvider(idpHost, {
|
||||
protocol: 'mock-idp.js?validatorAction=throw-error&errorInfo=bar',
|
||||
usernameHint: `alice@${idpDomain}`
|
||||
});
|
||||
|
||||
return pc1.createOffer()
|
||||
.then(offer => Promise.all([
|
||||
assert_rtcerror_rejection('idp-execution-failure',
|
||||
pc2.setRemoteDescription(offer)),
|
||||
assert_rtcerror_rejection('idp-execution-failure',
|
||||
pc2.peerIdentity)
|
||||
]))
|
||||
.then(() => {
|
||||
assert_equals(pc2.idpErrorInfo, 'bar',
|
||||
'Expect pc2.idpErrorInfo to be set to the err.idpErrorInfo thrown by mock-idp.js');
|
||||
});
|
||||
}, `When IdP throws error and pc has target peer identity, setRemoteDescription() and peerIdentity rejected with RTCError('idp-execution-error')`);
|
||||
|
||||
/*
|
||||
4.3.2. setRemoteDescription
|
||||
If there is no target peer identity, then setRemoteDescription does not await the
|
||||
completion of identity validation.
|
||||
|
||||
9.5. IdP Error Handling
|
||||
- If an identity provider throws an exception or returns a promise that is
|
||||
ultimately rejected, then the procedure that depends on the IdP MUST also fail.
|
||||
These types of errors will cause an IdP failure with an RTCError with errorDetail
|
||||
set to "idp-execution-failure".
|
||||
|
||||
9.4. Verifying Identity Assertions
|
||||
If identity validation fails and there is no a target peer identity, the value of
|
||||
the peerIdentity MUST be set to a new, unresolved promise instance. This permits
|
||||
the use of renegotiation (or a subsequent answer, if the session description was
|
||||
a provisional answer) to resolve or reject the identity.
|
||||
*/
|
||||
promise_test(t => {
|
||||
const pc1 = new RTCPeerConnection();
|
||||
t.add_cleanup(() => pc1.close());
|
||||
const pc2 = new RTCPeerConnection();
|
||||
|
||||
t.add_cleanup(() => pc2.close());
|
||||
|
||||
const port = window.location.port;
|
||||
const [idpDomain] = getIdpDomains();
|
||||
const idpHost = hostString(idpDomain, port);
|
||||
|
||||
// Ask mock-idp.js to throw error during validation,
|
||||
// i.e. during pc2.setRemoteDescription()
|
||||
pc1.setIdentityProvider(idpHost, {
|
||||
protocol: 'mock-idp.js?validatorAction=throw-error',
|
||||
usernameHint: `alice@${idpDomain}`
|
||||
});
|
||||
|
||||
const peerIdentityPromise1 = pc2.peerIdentity;
|
||||
|
||||
return pc1.createOffer()
|
||||
.then(offer =>
|
||||
// setRemoteDescription should succeed because there is no target peer identity set
|
||||
pc2.setRemoteDescription(offer))
|
||||
.then(() =>
|
||||
assert_rtcerror_rejection('idp-execution-failure',
|
||||
peerIdentityPromise1,
|
||||
`Expect first peerIdentity promise to be rejected with RTCError('idp-execution-failure')`))
|
||||
.then(() => {
|
||||
const peerIdentityPromise2 = pc2.peerIdentity;
|
||||
assert_not_equals(peerIdentityPromise2, peerIdentityPromise1,
|
||||
'Expect pc2.peerIdentity to be replaced with a fresh unresolved promise');
|
||||
|
||||
// regenerate an identity assertion with no test option to throw error
|
||||
pc1.setIdentityProvider(idpHost, {
|
||||
protocol: 'idp-test.js',
|
||||
usernameHint: `alice@${idpDomain}`
|
||||
});
|
||||
|
||||
return pc1.createOffer()
|
||||
.then(offer => pc2.setRemoteDescription(offer))
|
||||
.then(peerIdentityPromise2)
|
||||
.then(identityAssertion => {
|
||||
const { idp, name } = identityAssertion;
|
||||
|
||||
assert_equals(idp, idpDomain,
|
||||
`Expect IdP domain to be ${idpDomain}`);
|
||||
|
||||
assert_equals(name, `alice@${idpDomain}`,
|
||||
`Expect validated identity to be alice@${idpDomain}`);
|
||||
|
||||
assert_equals(pc2.peeridentity, peerIdentityPromise2,
|
||||
'Expect pc2.peerIdentity to stay fixed after identity is validated');
|
||||
});
|
||||
});
|
||||
}, 'IdP failure with no target peer identity should have following setRemoteDescription() succeed and replace pc.peerIdentity with a new promise');
|
||||
|
||||
</script>
|
|
@ -92,11 +92,11 @@ both the sdpMid and sdpMLineIndex dictionary members are null, throw a TypeError
|
|||
const candidate = "";
|
||||
const sdpMid = "sdpMid";
|
||||
const sdpMLineIndex = 1;
|
||||
const ufrag = "";
|
||||
const usernameFragment = "";
|
||||
const url = "foo.bar";
|
||||
|
||||
test(() => {
|
||||
const iceCandidate = new RTCIceCandidate({ candidate, sdpMid, sdpMLineIndex, ufrag });
|
||||
const iceCandidate = new RTCIceCandidate({ candidate, sdpMid, sdpMLineIndex, usernameFragment });
|
||||
const event = new RTCPeerConnectionIceEvent("type", {
|
||||
candidate: iceCandidate,
|
||||
url,
|
||||
|
@ -108,33 +108,10 @@ test(() => {
|
|||
}, "RTCPeerConnectionIceEvent with RTCIceCandidate");
|
||||
|
||||
test(() => {
|
||||
const plain = { candidate, sdpMid, sdpMLineIndex, ufrag };
|
||||
const plain = { candidate, sdpMid, sdpMLineIndex, usernameFragment };
|
||||
assert_throws(new TypeError(), () => new RTCPeerConnectionIceEvent("type", { candidate: plain }));
|
||||
}, "RTCPeerConnectionIceEvent with non RTCIceCandidate object throws");
|
||||
|
||||
/*
|
||||
This will remain commented out until https://github.com/w3c/webrtc-pc/issues/1232
|
||||
is resolved.
|
||||
|
||||
test(() => {
|
||||
// When firing an RTCPeerConnectionIceEvent event that contains a RTCIceCandidate
|
||||
// object, it must include values for both sdpMid and sdpMLineIndex.
|
||||
|
||||
assert_throws(new TypeError(), () => {
|
||||
new RTCPeerConnectionIceEvent("type", {
|
||||
candidate: new RTCIceCandidate({ candidate, sdpMid, ufrag })
|
||||
});
|
||||
});
|
||||
|
||||
assert_throws(new TypeError(), () => {
|
||||
new RTCPeerConnectionIceEvent("type", {
|
||||
candidate: new RTCIceCandidate({ candidate, sdpMLineIndex, ufrag })
|
||||
});
|
||||
});
|
||||
|
||||
}, "RTCIceCandidate must include values for both sdpMid and sdpMLineIndex");
|
||||
*/
|
||||
|
||||
test(() => {
|
||||
const event = new RTCPeerConnectionIceEvent("type", {
|
||||
candidate: null,
|
||||
|
@ -145,4 +122,5 @@ test(() => {
|
|||
assert_true(event.bubbles);
|
||||
assert_true(event.cancelable);
|
||||
}, "RTCPeerConnectionIceEvent bubbles and cancelable");
|
||||
|
||||
</script>
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
/*
|
||||
In web-platform-test, a number of domains are required to be set up locally.
|
||||
The list is available at docs/_writing-tests/server-features.md. The
|
||||
appropriate hosts file entries can be generated with the WPT CLI via the
|
||||
following command: `wpt make-hosts-file`.
|
||||
*/
|
||||
|
||||
/*
|
||||
dictionary RTCIdentityProviderDetails {
|
||||
required DOMString domain;
|
||||
DOMString protocol = "default";
|
||||
};
|
||||
*/
|
||||
|
||||
// Parse a base64 JSON encoded string returned from getIdentityAssertion().
|
||||
// This is also the string that is set in the a=identity line.
|
||||
// Returns a { idp, assertion } where idp is of type RTCIdentityProviderDetails
|
||||
// and assertion is the deserialized JSON that was returned by the
|
||||
// IdP proxy's generateAssertion() function.
|
||||
function parseAssertionResult(assertionResultStr) {
|
||||
const assertionResult = JSON.parse(atob(assertionResultStr));
|
||||
|
||||
const { idp } = assertionResult;
|
||||
const assertion = JSON.parse(assertionResult.assertion);
|
||||
|
||||
return { idp, assertion };
|
||||
}
|
||||
|
||||
// Return two distinct IdP domains that are different from current domain
|
||||
function getIdpDomains() {
|
||||
const domainA = '{{domains[www]}}';
|
||||
const domainB = '{{domains[www1]}}';
|
||||
const domainC = '{{domains[www2]}}';
|
||||
|
||||
if(window.location.hostname === domainA) {
|
||||
return [domainB, domainC];
|
||||
} else if(window.location.hostname === domainB) {
|
||||
return [domainA, domainC];
|
||||
} else {
|
||||
return [domainA, domainB];
|
||||
}
|
||||
}
|
||||
|
||||
function assert_rtcerror_rejection(errorDetail, promise, desc) {
|
||||
return promise.then(
|
||||
res => {
|
||||
assert_unreached(`Expect promise to be rejected with RTCError, but instead got ${res}`);
|
||||
}, err => {
|
||||
assert_true(err instanceof RTCError,
|
||||
'Expect error object to be instance of RTCError');
|
||||
|
||||
assert_equals(err.errorDetail, errorDetail,
|
||||
`Expect RTCError object have errorDetail set to ${errorDetail}`);
|
||||
|
||||
return err;
|
||||
});
|
||||
}
|
||||
|
||||
// construct a host string consist of domain and optionally port
|
||||
// If the default HTTP/HTTPS port is used, window.location.port returns
|
||||
// empty string.
|
||||
function hostString(domain, port) {
|
||||
if(port === '') {
|
||||
return domain;
|
||||
} else {
|
||||
return `${domain}:${port}`;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<!doctype html>
|
||||
<script>
|
||||
window.onmessage = async (event) => {
|
||||
let certificate = event.data;
|
||||
if (!certificate)
|
||||
certificate = await RTCPeerConnection.generateCertificate({ name: 'ECDSA', namedCurve: 'P-256'});
|
||||
event.source.postMessage(certificate, "*");
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,122 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>RTCPeerConnection Connection Test</title>
|
||||
<script src="RTCPeerConnection-helper.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<div>
|
||||
<video id="local-view" muted autoplay="autoplay"></video>
|
||||
<video id="remote-view" muted autoplay="autoplay"/>
|
||||
</video>
|
||||
</div>
|
||||
|
||||
<!-- These files are in place when executing on W3C. -->
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script type="text/javascript">
|
||||
var test = async_test('Can set up a basic WebRTC call without announcing ssrcs.', {timeout: 5000});
|
||||
|
||||
var gFirstConnection = null;
|
||||
var gSecondConnection = null;
|
||||
|
||||
// if the remote video gets video data that implies the negotiation
|
||||
// as well as the ICE and DTLS connection are up.
|
||||
document.getElementById('remote-view')
|
||||
.addEventListener('loadedmetadata', function() {
|
||||
// Call negotiated: done.
|
||||
test.done();
|
||||
});
|
||||
|
||||
function getNoiseStreamOkCallback(localStream) {
|
||||
gFirstConnection = new RTCPeerConnection(null);
|
||||
gFirstConnection.onicecandidate = onIceCandidateToFirst;
|
||||
localStream.getTracks().forEach(function(track) {
|
||||
gFirstConnection.addTrack(track, localStream);
|
||||
});
|
||||
gFirstConnection.createOffer().then(onOfferCreated, failed('createOffer'));
|
||||
|
||||
var videoTag = document.getElementById('local-view');
|
||||
videoTag.srcObject = localStream;
|
||||
};
|
||||
|
||||
var onOfferCreated = test.step_func(function(offer) {
|
||||
gFirstConnection.setLocalDescription(offer);
|
||||
|
||||
// remove all a=ssrc: lines, the msid-semantic line and any a=msid:.
|
||||
var sdp = offer.sdp.replace(/^a=ssrc:.*$\r\n/gm, '')
|
||||
.replace(/^a=msid-semantic.*$\r\n/gm, '')
|
||||
.replace(/^a=msid:.*$\r\n/gm, '');
|
||||
|
||||
// This would normally go across the application's signaling solution.
|
||||
// In our case, the "signaling" is to call this function.
|
||||
receiveCall(sdp);
|
||||
});
|
||||
|
||||
function receiveCall(offerSdp) {
|
||||
gSecondConnection = new RTCPeerConnection(null);
|
||||
gSecondConnection.onicecandidate = onIceCandidateToSecond;
|
||||
gSecondConnection.ontrack = onRemoteTrack;
|
||||
|
||||
var parsedOffer = new RTCSessionDescription({ type: 'offer',
|
||||
sdp: offerSdp });
|
||||
gSecondConnection.setRemoteDescription(parsedOffer);
|
||||
|
||||
gSecondConnection.createAnswer().then(onAnswerCreated,
|
||||
failed('createAnswer'));
|
||||
};
|
||||
|
||||
var onAnswerCreated = test.step_func(function(answer) {
|
||||
gSecondConnection.setLocalDescription(answer);
|
||||
|
||||
// remove all a=ssrc: lines, the msid-semantic line and any a=msid:.
|
||||
var sdp = answer.sdp.replace(/^a=ssrc:.*$\r\n/gm, '')
|
||||
.replace(/^a=msid-semantic.*$\r\n/gm, '')
|
||||
.replace(/^a=msid:.*$\r\n/gm, '');
|
||||
|
||||
// Similarly, this would go over the application's signaling solution.
|
||||
handleAnswer(sdp);
|
||||
});
|
||||
|
||||
function handleAnswer(answerSdp) {
|
||||
var parsedAnswer = new RTCSessionDescription({ type: 'answer',
|
||||
sdp: answerSdp });
|
||||
gFirstConnection.setRemoteDescription(parsedAnswer);
|
||||
};
|
||||
|
||||
var onIceCandidateToFirst = test.step_func(function(event) {
|
||||
// If event.candidate is null = no more candidates.
|
||||
if (event.candidate) {
|
||||
gSecondConnection.addIceCandidate(event.candidate);
|
||||
}
|
||||
});
|
||||
|
||||
var onIceCandidateToSecond = test.step_func(function(event) {
|
||||
if (event.candidate) {
|
||||
gFirstConnection.addIceCandidate(event.candidate);
|
||||
}
|
||||
});
|
||||
|
||||
var onRemoteTrack = test.step_func(function(event) {
|
||||
var videoTag = document.getElementById('remote-view');
|
||||
if (!videoTag.srcObject) {
|
||||
videoTag.srcObject = event.streams[0];
|
||||
}
|
||||
});
|
||||
|
||||
// Returns a suitable error callback.
|
||||
function failed(function_name) {
|
||||
return test.unreached_func('WebRTC called error callback for ' + function_name);
|
||||
}
|
||||
|
||||
// This function starts the test.
|
||||
test.step(function() {
|
||||
getNoiseStream({ video: true, audio: true })
|
||||
.then(test.step_func(getNoiseStreamOkCallback), failed('getNoiseStream'));
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
Loading…
Add table
Add a link
Reference in a new issue