Update web-platform-tests to revision 6e9693d2690e0648fb9a1bd902af7cc078f28515

This commit is contained in:
WPT Sync Bot 2018-11-03 21:29:40 -04:00
parent 4ec7dedce1
commit 612038c4d6
56 changed files with 1374 additions and 477 deletions

View file

@ -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>

View file

@ -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

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -358,7 +358,7 @@ const trackFactories = {
*/
canCreate(requested) {
const supported = {
audio: !!window.MediaStreamAudioDestinationNode,
audio: !!window.AudioContext && !!window.MediaStreamAudioDestinationNode,
video: !!HTMLCanvasElement.prototype.captureStream
};

View file

@ -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>

View file

@ -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>

View file

@ -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}`;
}
}

View file

@ -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>

View file

@ -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>