| <!doctype html> | 
 | <title>RTCConfiguration rtcpMuxPolicy</title> | 
 | <script src="/resources/testharness.js"></script> | 
 | <script src="/resources/testharnessreport.js"></script> | 
 | <script src="RTCConfiguration-helper.js"></script> | 
 | <script src="RTCPeerConnection-helper.js"></script> | 
 | <script> | 
 |  'use strict'; | 
 |  | 
 |  // Test is based on the following editor draft: | 
 |  // https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.html | 
 |  | 
 |  // The following helper function is called from RTCConfiguration-helper.js: | 
 |  // config_test | 
 |  | 
 |  /* | 
 |  [Constructor(optional RTCConfiguration configuration)] | 
 |  interface RTCPeerConnection : EventTarget { | 
 |  RTCConfiguration getConfiguration(); | 
 |  void setConfiguration(RTCConfiguration configuration); | 
 |  ... | 
 |  }; | 
 |  | 
 |  dictionary RTCConfiguration { | 
 |  RTCRtcpMuxPolicy rtcpMuxPolicy = "require"; | 
 |  ... | 
 |  }; | 
 |  | 
 |  enum RTCRtcpMuxPolicy { | 
 |  "negotiate", | 
 |  "require" | 
 |  }; | 
 |  */ | 
 |  | 
 |  test(() => { | 
 |  const pc = new RTCPeerConnection(); | 
 |  assert_equals(pc.getConfiguration().rtcpMuxPolicy, 'require'); | 
 |  }, `new RTCPeerConnection() should have default rtcpMuxPolicy require`); | 
 |  | 
 |  test(() => { | 
 |  const pc = new RTCPeerConnection({ rtcpMuxPolicy: undefined }); | 
 |  assert_equals(pc.getConfiguration().rtcpMuxPolicy, 'require'); | 
 |  }, `new RTCPeerConnection({ rtcpMuxPolicy: undefined }) should have default rtcpMuxPolicy require`); | 
 |  | 
 |  test(() => { | 
 |  const pc = new RTCPeerConnection({ rtcpMuxPolicy: 'require' }); | 
 |  assert_equals(pc.getConfiguration().rtcpMuxPolicy, 'require'); | 
 |  }, `new RTCPeerConnection({ rtcpMuxPolicy: 'require' }) should succeed`); | 
 |  | 
 |  /* | 
 |  4.3.1.1. Constructor | 
 |  3. If configuration.rtcpMuxPolicy is negotiate, and the user agent does not | 
 |  implement non-muxed RTCP, throw a NotSupportedError. | 
 |  */ | 
 |  test(() => { | 
 |  let pc; | 
 |  try { | 
 |  pc = new RTCPeerConnection({ rtcpMuxPolicy: 'negotiate' }); | 
 |  } catch(err) { | 
 |  // NotSupportedError is a DOMException with code 9 | 
 |  if(err.code === 9 && err.name === 'NotSupportedError') { | 
 |  // ignore error and pass test if negotiate is not supported | 
 |  return; | 
 |  } else { | 
 |  throw err; | 
 |  } | 
 |  } | 
 |  | 
 |  assert_equals(pc.getConfiguration().rtcpMuxPolicy, 'negotiate'); | 
 |  | 
 |  }, `new RTCPeerConnection({ rtcpMuxPolicy: 'negotiate' }) may succeed or throw NotSupportedError`); | 
 |  | 
 |  config_test(makePc => { | 
 |  assert_throws_js(TypeError, () => | 
 |  makePc({ rtcpMuxPolicy: null })); | 
 |  }, `with { rtcpMuxPolicy: null } should throw TypeError`); | 
 |  | 
 |  config_test(makePc => { | 
 |  assert_throws_js(TypeError, () => | 
 |  makePc({ rtcpMuxPolicy: 'invalid' })); | 
 |  }, `with { rtcpMuxPolicy: 'invalid' } should throw TypeError`); | 
 |  | 
 |  /* | 
 |  4.3.2. Set a configuration | 
 |  6. If configuration.rtcpMuxPolicy is set and its value differs from the | 
 |  connection's rtcpMux policy, throw an InvalidModificationError. | 
 |  */ | 
 |  | 
 |  test(() => { | 
 |  const pc = new RTCPeerConnection({ rtcpMuxPolicy: 'require' }); | 
 |  assert_idl_attribute(pc, 'setConfiguration'); | 
 |  assert_throws('InvalidModificationError', () => | 
 |  pc.setConfiguration({ rtcpMuxPolicy: 'negotiate' })); | 
 |  | 
 |  }, `setConfiguration({ rtcpMuxPolicy: 'negotiate' }) with initial rtcpMuxPolicy require should throw InvalidModificationError`); | 
 |  | 
 |  test(() => { | 
 |  let pc; | 
 |  try { | 
 |  pc = new RTCPeerConnection({ rtcpMuxPolicy: 'negotiate' }); | 
 |  } catch(err) { | 
 |  // NotSupportedError is a DOMException with code 9 | 
 |  if(err.code === 9 && err.name === 'NotSupportedError') { | 
 |  // ignore error and pass test if negotiate is not supported | 
 |  return; | 
 |  } else { | 
 |  throw err; | 
 |  } | 
 |  } | 
 |  | 
 |  assert_idl_attribute(pc, 'setConfiguration'); | 
 |  assert_throws('InvalidModificationError', () => | 
 |  pc.setConfiguration({ rtcpMuxPolicy: 'require' })); | 
 |  | 
 |  }, `setConfiguration({ rtcpMuxPolicy: 'require' }) with initial rtcpMuxPolicy negotiate should throw InvalidModificationError`); | 
 |  | 
 |  test(() => { | 
 |  let pc; | 
 |  try { | 
 |  pc = new RTCPeerConnection({ rtcpMuxPolicy: 'negotiate' }); | 
 |  } catch(err) { | 
 |  // NotSupportedError is a DOMException with code 9 | 
 |  if(err.code === 9 && err.name === 'NotSupportedError') { | 
 |  // ignore error and pass test if negotiate is not supported | 
 |  return; | 
 |  } else { | 
 |  throw err; | 
 |  } | 
 |  } | 
 |  | 
 |  assert_idl_attribute(pc, 'setConfiguration'); | 
 |  // default value for rtcpMuxPolicy is require | 
 |  assert_throws('InvalidModificationError', () => | 
 |  pc.setConfiguration({})); | 
 |  | 
 |  }, `setConfiguration({}) with initial rtcpMuxPolicy negotiate should throw InvalidModificationError`); | 
 |  | 
 |  /* | 
 |  Coverage Report | 
 |  | 
 |  Tested 2 | 
 |  Total 2 | 
 |  */ | 
 |  const FINGERPRINT_SHA256 = '00:00:00:00:00:00:00:00:00:00:00:00:00' + | 
 |  ':00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00'; | 
 |  const ICEUFRAG = 'someufrag'; | 
 |  const ICEPWD = 'somelongpwdwithenoughrandomness'; | 
 |  | 
 |  promise_test(async t => { | 
 |  // audio-only SDP offer without BUNDLE and rtcp-mux. | 
 |  const sdp = 'v=0\r\n' + | 
 |  'o=- 166855176514521964 2 IN IP4 127.0.0.1\r\n' + | 
 |  's=-\r\n' + | 
 |  't=0 0\r\n' + | 
 |  'm=audio 9 UDP/TLS/RTP/SAVPF 111\r\n' + | 
 |  'c=IN IP4 0.0.0.0\r\n' + | 
 |  'a=rtcp:9 IN IP4 0.0.0.0\r\n' + | 
 |  'a=ice-ufrag:' + ICEUFRAG + '\r\n' + | 
 |  'a=ice-pwd:' + ICEPWD + '\r\n' + | 
 |  'a=fingerprint:sha-256 ' + FINGERPRINT_SHA256 + '\r\n' + | 
 |  'a=setup:actpass\r\n' + | 
 |  'a=mid:audio1\r\n' + | 
 |  'a=sendonly\r\n' + | 
 |  'a=rtcp-rsize\r\n' + | 
 |  'a=rtpmap:111 opus/48000/2\r\n'; | 
 |  const pc = new RTCPeerConnection({rtcpMuxPolicy: 'require'}); | 
 |  t.add_cleanup(() => pc.close()); | 
 |  | 
 |  return promise_rejects(t, 'InvalidAccessError', pc.setRemoteDescription({type: 'offer', sdp})); | 
 |  }, 'setRemoteDescription throws InvalidAccessError when called with an offer without rtcp-mux and rtcpMuxPolicy is set to require'); | 
 |  | 
 |  promise_test(async t => { | 
 |  // audio-only SDP answer without BUNDLE and rtcp-mux. | 
 |  // Also omitting a=mid in order to avoid parsing it from the offer as this needs to match. | 
 |  const sdp = 'v=0\r\n' + | 
 |  'o=- 166855176514521964 2 IN IP4 127.0.0.1\r\n' + | 
 |  's=-\r\n' + | 
 |  't=0 0\r\n' + | 
 |  'm=audio 9 UDP/TLS/RTP/SAVPF 111\r\n' + | 
 |  'c=IN IP4 0.0.0.0\r\n' + | 
 |  'a=rtcp:9 IN IP4 0.0.0.0\r\n' + | 
 |  'a=ice-ufrag:' + ICEUFRAG + '\r\n' + | 
 |  'a=ice-pwd:' + ICEPWD + '\r\n' + | 
 |  'a=fingerprint:sha-256 ' + FINGERPRINT_SHA256 + '\r\n' + | 
 |  'a=setup:active\r\n' + | 
 |  'a=sendonly\r\n' + | 
 |  'a=rtcp-rsize\r\n' + | 
 |  'a=rtpmap:111 opus/48000/2\r\n'; | 
 |  const pc = new RTCPeerConnection({rtcpMuxPolicy: 'require'}); | 
 |  t.add_cleanup(() => pc.close()); | 
 |  | 
 |  const offer = await generateAudioReceiveOnlyOffer(pc); | 
 |  await pc.setLocalDescription(offer); | 
 |  return promise_rejects(t, 'InvalidAccessError', pc.setRemoteDescription({type: 'answer', sdp})); | 
 |  }, 'setRemoteDescription throws InvalidAccessError when called with an answer without rtcp-mux and rtcpMuxPolicy is set to require'); | 
 | </script> |