tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

RTCRtpTransceiver-headerExtensionControl.html (15789B)


      1 <!doctype html>
      2 <meta charset=utf-8>
      3 <title>RTCRtpParameters encodings</title>
      4 <script src="/resources/testharness.js"></script>
      5 <script src="/resources/testharnessreport.js"></script>
      6 <script src="/webrtc/dictionary-helper.js"></script>
      7 <script src="/webrtc/RTCRtpParameters-helper.js"></script>
      8 <script src="/webrtc/third_party/sdp/sdp.js"></script>
      9 <script>
     10 'use strict';
     11 
     12 async function negotiate(pc1, pc2) {
     13  await pc1.setLocalDescription();
     14  await pc2.setRemoteDescription(pc1.localDescription);
     15  await pc2.setLocalDescription();
     16  await pc1.setRemoteDescription(pc2.localDescription);
     17 }
     18 
     19 ['audio', 'video'].forEach(kind => {
     20  test(t => {
     21    const pc = new RTCPeerConnection();
     22    t.add_cleanup(() => pc.close());
     23    const transceiver = pc.addTransceiver(kind);
     24    const capabilities = transceiver.getHeaderExtensionsToNegotiate();
     25    const capability = capabilities.find((capability) => {
     26        return capability.uri === 'urn:ietf:params:rtp-hdrext:sdes:mid';
     27    });
     28    assert_not_equals(capability, undefined);
     29    assert_equals(capability.direction, 'sendrecv');
     30  }, `the ${kind} transceiver.getHeaderExtensionsToNegotiate() includes mandatory extensions`);
     31 });
     32 
     33 test(t => {
     34  const pc = new RTCPeerConnection();
     35  t.add_cleanup(() => pc.close());
     36  const transceiver = pc.addTransceiver('audio');
     37  const capabilities = transceiver.getHeaderExtensionsToNegotiate();
     38  capabilities[0].uri = '';
     39  assert_throws_js(TypeError, () => {
     40    transceiver.setHeaderExtensionsToNegotiate(capabilities);
     41  }, 'transceiver should throw TypeError when setting an empty URI');
     42 }, `setHeaderExtensionsToNegotiate throws TypeError on encountering missing URI`);
     43 
     44 test(t => {
     45  const pc = new RTCPeerConnection();
     46  t.add_cleanup(() => pc.close());
     47  const transceiver = pc.addTransceiver('audio');
     48  const capabilities = transceiver.getHeaderExtensionsToNegotiate();
     49  capabilities[0].direction = '';
     50  assert_throws_js(TypeError, () => {
     51    transceiver.setHeaderExtensionsToNegotiate(capabilities);
     52  }, 'transceiver should throw TypeError when setting an empty direction');
     53 }, `setHeaderExtensionsToNegotiate throws TypeError on encountering missing direction`);
     54 
     55 test(t => {
     56  const pc = new RTCPeerConnection();
     57  t.add_cleanup(() => pc.close());
     58  const transceiver = pc.addTransceiver('audio');
     59  const capabilities = transceiver.getHeaderExtensionsToNegotiate();
     60  capabilities[0].uri = '4711';
     61  assert_throws_dom('InvalidModificationError', () => {
     62    transceiver.setHeaderExtensionsToNegotiate(capabilities);
     63  }, 'transceiver should throw InvalidModificationError when setting an unknown URI');
     64 }, `setHeaderExtensionsToNegotiate throws InvalidModificationError on encountering unknown URI`);
     65 
     66 test(t => {
     67  const pc = new RTCPeerConnection();
     68  t.add_cleanup(() => pc.close());
     69  const transceiver = pc.addTransceiver('video');
     70  const capabilities = transceiver.getHeaderExtensionsToNegotiate().filter(capability => {
     71    return capability.uri === 'urn:ietf:params:rtp-hdrext:sdes:mid';
     72  });
     73  assert_throws_dom('InvalidModificationError', () => {
     74    transceiver.setHeaderExtensionsToNegotiate(capabilities);
     75  }, 'transceiver should throw InvalidModificationError when removing elements from the list');
     76 }, `setHeaderExtensionsToNegotiate throws InvalidModificationError when removing elements from the list`);
     77 
     78 test(t => {
     79  const pc = new RTCPeerConnection();
     80  t.add_cleanup(() => pc.close());
     81  const transceiver = pc.addTransceiver('video');
     82  const capabilities = transceiver.getHeaderExtensionsToNegotiate();
     83  capabilities.push({
     84    uri: '4711',
     85    direction: 'recvonly',
     86  });
     87  assert_throws_dom('InvalidModificationError', () => {
     88    transceiver.setHeaderExtensionsToNegotiate(capabilities);
     89  }, 'transceiver should throw InvalidModificationError when adding elements to the list');
     90 }, `setHeaderExtensionsToNegotiate throws InvalidModificationError when adding elements to the list`);
     91 
     92 test(t => {
     93  const pc = new RTCPeerConnection();
     94  t.add_cleanup(() => pc.close());
     95  const transceiver = pc.addTransceiver('audio');
     96  const capabilities = transceiver.getHeaderExtensionsToNegotiate();
     97  const capability = capabilities.find((capability) => {
     98      return capability.uri === 'urn:ietf:params:rtp-hdrext:sdes:mid';
     99  });
    100  ['sendonly', 'recvonly', 'inactive', 'stopped'].map(direction => {
    101    capability.direction = direction;
    102    assert_throws_dom('InvalidModificationError', () => {
    103      transceiver.setHeaderExtensionsToNegotiate(capabilities);
    104    }, `transceiver should throw InvalidModificationError when setting a mandatory header extension\'s direction to ${direction}`);
    105  });
    106 }, `setHeaderExtensionsToNegotiate throws InvalidModificationError when setting a mandatory header extension\'s direction to something else than "sendrecv"`);
    107 
    108 test(t => {
    109  const pc = new RTCPeerConnection();
    110  t.add_cleanup(() => pc.close());
    111  const transceiver = pc.addTransceiver('audio');
    112  const capabilities = transceiver.getHeaderExtensionsToNegotiate();
    113  const selected_capability = capabilities.find((capability) => {
    114      return capability.direction === 'sendrecv' &&
    115             capability.uri !== 'urn:ietf:params:rtp-hdrext:sdes:mid';
    116  });
    117  selected_capability.direction = 'stopped';
    118  const offered_capabilities = transceiver.getHeaderExtensionsToNegotiate();
    119  const altered_capability = capabilities.find((capability) => {
    120      return capability.uri === selected_capability.uri &&
    121             capability.direction === 'stopped';
    122  });
    123  assert_not_equals(altered_capability, undefined);
    124 }, `modified direction set by setHeaderExtensionsToNegotiate is visible in subsequent getHeaderExtensionsToNegotiate`);
    125 
    126 promise_test(async t => {
    127  const pc = new RTCPeerConnection();
    128  t.add_cleanup(() => pc.close());
    129  const transceiver = pc.addTransceiver('video');
    130  const capabilities = transceiver.getHeaderExtensionsToNegotiate();
    131  const offer = await pc.createOffer();
    132  const extensions = SDPUtils.matchPrefix(SDPUtils.splitSections(offer.sdp)[1], 'a=extmap:')
    133    .map(line => SDPUtils.parseExtmap(line));
    134  for (const capability of capabilities) {
    135    if (capability.direction === 'stopped') {
    136      assert_equals(undefined, extensions.find(e => e.uri === capability.uri));
    137    } else {
    138      assert_not_equals(undefined, extensions.find(e => e.uri === capability.uri));
    139    }
    140  }
    141 }, `Unstopped extensions turn up in offer`);
    142 
    143 promise_test(async t => {
    144  const pc = new RTCPeerConnection();
    145  t.add_cleanup(() => pc.close());
    146  const transceiver = pc.addTransceiver('video');
    147  const capabilities = transceiver.getHeaderExtensionsToNegotiate();
    148  const selected_capability = capabilities.find((capability) => {
    149      return capability.direction === 'sendrecv' &&
    150             capability.uri !== 'urn:ietf:params:rtp-hdrext:sdes:mid';
    151  });
    152  selected_capability.direction = 'stopped';
    153  transceiver.setHeaderExtensionsToNegotiate(capabilities);
    154  const offer = await pc.createOffer();
    155  const extensions = SDPUtils.matchPrefix(SDPUtils.splitSections(offer.sdp)[1], 'a=extmap:')
    156    .map(line => SDPUtils.parseExtmap(line));
    157  for (const capability of capabilities) {
    158    if (capability.direction === 'stopped') {
    159      assert_equals(undefined, extensions.find(e => e.uri === capability.uri));
    160    } else {
    161      assert_not_equals(undefined, extensions.find(e => e.uri === capability.uri));
    162    }
    163  }
    164 }, `Stopped extensions do not turn up in offers`);
    165 
    166 promise_test(async t => {
    167  const pc1 = new RTCPeerConnection();
    168  t.add_cleanup(() => pc1.close());
    169  const pc2 = new RTCPeerConnection();
    170  t.add_cleanup(() => pc2.close());
    171 
    172  // Disable a non-mandatory extension before first negotiation.
    173  const transceiver = pc1.addTransceiver('video');
    174  const capabilities = transceiver.getHeaderExtensionsToNegotiate();
    175  const selected_capability = capabilities.find((capability) => {
    176      return capability.direction === 'sendrecv' &&
    177             capability.uri !== 'urn:ietf:params:rtp-hdrext:sdes:mid';
    178  });
    179  selected_capability.direction = 'stopped';
    180  transceiver.setHeaderExtensionsToNegotiate(capabilities);
    181 
    182  await negotiate(pc1, pc2);
    183  const negotiated_capabilites = transceiver.getNegotiatedHeaderExtensions();
    184 
    185  assert_equals(capabilities.length, negotiated_capabilites.length);
    186 }, `The set of negotiated extensions has the same size as the set of extensions to negotiate`);
    187 
    188 promise_test(async t => {
    189  const pc1 = new RTCPeerConnection();
    190  t.add_cleanup(() => pc1.close());
    191  const pc2 = new RTCPeerConnection();
    192  t.add_cleanup(() => pc2.close());
    193 
    194  // Disable a non-mandatory extension before first negotiation.
    195  const transceiver = pc1.addTransceiver('video');
    196  const capabilities = transceiver.getHeaderExtensionsToNegotiate();
    197  const selected_capability = capabilities.find((capability) => {
    198    return capability.direction === 'sendrecv' &&
    199           capability.uri !== 'urn:ietf:params:rtp-hdrext:sdes:mid';
    200  });
    201  selected_capability.direction = 'stopped';
    202  transceiver.setHeaderExtensionsToNegotiate(capabilities);
    203 
    204  await negotiate(pc1, pc2);
    205  const negotiated_capabilities = transceiver.getNegotiatedHeaderExtensions();
    206 
    207  // Attempt enabling the extension.
    208  const selected_capability_2 = negotiated_capabilities.find((capability) => {
    209    return capability.uri === selected_capability.uri;
    210  });
    211  selected_capability_2.direction = 'sendrecv';
    212  // The enabled extension should not be part of the negotiated set.
    213  transceiver.setHeaderExtensionsToNegotiate(negotiated_capabilities);
    214  await negotiate(pc1, pc2);
    215  assert_equals(
    216      transceiver.getNegotiatedHeaderExtensions().find(capability => {
    217        return capability.uri === selected_capability.uri
    218      }).direction, 'sendrecv');
    219 }, `Header extensions can be reactivated in subsequent offers`);
    220 
    221 promise_test(async t => {
    222  const pc = new RTCPeerConnection();
    223  t.add_cleanup(() => pc.close());
    224 
    225  const t1 = pc.addTransceiver('video');
    226  const t2 = pc.addTransceiver('video');
    227  const extensionUri = 'urn:3gpp:video-orientation';
    228 
    229  assert_true(!!t1.getHeaderExtensionsToNegotiate().find(ext => ext.uri === extensionUri));
    230  const ext1 = t1.getHeaderExtensionsToNegotiate();
    231  ext1.find(ext => ext.uri === extensionUri).direction = 'stopped';
    232  t1.setHeaderExtensionsToNegotiate(ext1);
    233 
    234  assert_true(!!t2.getHeaderExtensionsToNegotiate().find(ext => ext.uri === extensionUri));
    235  const ext2 = t2.getHeaderExtensionsToNegotiate();
    236  ext2.find(ext => ext.uri === extensionUri).direction = 'sendrecv';
    237  t2.setHeaderExtensionsToNegotiate(ext2);
    238 
    239  const offer = await pc.createOffer();
    240  const sections = SDPUtils.splitSections(offer.sdp);
    241  sections.shift();
    242  const extensions = sections.map(section => {
    243    return SDPUtils.matchPrefix(section, 'a=extmap:')
    244      .map(SDPUtils.parseExtmap);
    245  });
    246  assert_equals(extensions.length, 2);
    247  assert_false(!!extensions[0].find(extension => extension.uri === extensionUri));
    248  assert_true(!!extensions[1].find(extension => extension.uri === extensionUri));
    249 }, 'Header extensions can be deactivated on a per-mline basis');
    250 
    251 promise_test(async t => {
    252  const pc1 = new RTCPeerConnection();
    253  t.add_cleanup(() => pc1.close());
    254  const pc2 = new RTCPeerConnection();
    255  t.add_cleanup(() => pc2.close());
    256 
    257  const t1 = pc1.addTransceiver('video');
    258 
    259  await pc1.setLocalDescription();
    260  await pc2.setRemoteDescription(pc1.localDescription);
    261  // Get the transceiver after it is created by SRD.
    262  const t2 = pc2.getTransceivers()[0];
    263  const t2_capabilities = t2.getHeaderExtensionsToNegotiate();
    264  const t2_capability_to_stop = t2_capabilities
    265    .find(capability => capability.uri !== 'urn:ietf:params:rtp-hdrext:sdes:mid');
    266  assert_not_equals(undefined, t2_capability_to_stop);
    267  t2_capability_to_stop.direction = 'stopped';
    268  t2.setHeaderExtensionsToNegotiate(t2_capabilities);
    269 
    270  await pc2.setLocalDescription();
    271  await pc1.setRemoteDescription(pc2.localDescription);
    272 
    273  const t1_negotiated = t1.getNegotiatedHeaderExtensions()
    274    .find(extension => extension.uri === t2_capability_to_stop.uri);
    275  assert_not_equals(undefined, t1_negotiated);
    276  assert_equals(t1_negotiated.direction, 'stopped', 'in negotiated');
    277  // The negotiated extension is reflected into HeaderExtensionsToNegotiate
    278  const t1_capability = t1.getHeaderExtensionsToNegotiate()
    279    .find(extension => extension.uri === t2_capability_to_stop.uri);
    280  assert_not_equals(undefined, t1_capability);
    281  assert_equals(t1_capability.direction, 'stopped', 'in ToNegotiate');
    282 }, 'Extensions not negotiated by the peer are `stopped` in getNegotiatedHeaderExtensions');
    283 
    284 promise_test(async t => {
    285  const pc = new RTCPeerConnection();
    286  t.add_cleanup(() => pc.close());
    287 
    288  const transceiver = pc.addTransceiver('video');
    289  const negotiated_capabilites = transceiver.getNegotiatedHeaderExtensions();
    290  assert_equals(negotiated_capabilites.length,
    291                transceiver.getHeaderExtensionsToNegotiate().length);
    292  for (const capability of negotiated_capabilites) {
    293    assert_equals(capability.direction, 'stopped');
    294  }
    295 }, 'Prior to negotiation, getNegotiatedHeaderExtensions() returns `stopped` for all extensions.');
    296 
    297 promise_test(async t => {
    298  const pc1 = new RTCPeerConnection();
    299  t.add_cleanup(() => pc1.close());
    300  const pc2 = new RTCPeerConnection();
    301  t.add_cleanup(() => pc2.close());
    302 
    303  // Disable a non-mandatory extension before first negotiation.
    304  const transceiver = pc1.addTransceiver('video');
    305  const capabilities = transceiver.getHeaderExtensionsToNegotiate();
    306  const selected_capability = capabilities.find((capability) => {
    307      return capability.direction === 'sendrecv' &&
    308             capability.uri !== 'urn:ietf:params:rtp-hdrext:sdes:mid';
    309  });
    310  selected_capability.direction = 'stopped';
    311  transceiver.setHeaderExtensionsToNegotiate(capabilities);
    312 
    313  await negotiate(pc1, pc2);
    314  const negotiated_capabilites = transceiver.getNegotiatedHeaderExtensions();
    315 
    316  const local_negotiated = transceiver.getNegotiatedHeaderExtensions().find(ext => {
    317    return ext.uri === selected_capability.uri;
    318  });
    319  assert_equals(local_negotiated.direction, 'stopped');
    320  const remote_negotiated = pc2.getTransceivers()[0].getNegotiatedHeaderExtensions().find(ext => {
    321    return ext.uri === selected_capability.uri;
    322  });
    323  assert_equals(remote_negotiated.direction, 'stopped');
    324 }, 'Answer header extensions are a subset of the offered header extensions');
    325 
    326 promise_test(async t => {
    327  const pc1 = new RTCPeerConnection();
    328  t.add_cleanup(() => pc1.close());
    329  const pc2 = new RTCPeerConnection();
    330  t.add_cleanup(() => pc2.close());
    331 
    332  // Disable a non-mandatory extension before first negotiation.
    333  const transceiver = pc1.addTransceiver('video');
    334  const capabilities = transceiver.getHeaderExtensionsToNegotiate();
    335  const selected_capability = capabilities.find((capability) => {
    336      return capability.direction === 'sendrecv' &&
    337             capability.uri !== 'urn:ietf:params:rtp-hdrext:sdes:mid';
    338  });
    339  selected_capability.direction = 'stopped';
    340  transceiver.setHeaderExtensionsToNegotiate(capabilities);
    341 
    342  await negotiate(pc1, pc2);
    343  // Negotiate, switching sides.
    344  await negotiate(pc2, pc1);
    345 
    346  // PC2 will NOT re-offer the extension.
    347  const remote_reoffered = pc2.getTransceivers()[0].getHeaderExtensionsToNegotiate().find(ext => {
    348    return ext.uri === selected_capability.uri;
    349  });
    350  assert_equals(remote_reoffered.direction, 'stopped', 'in pc2 ToNegotiate');
    351 
    352  const negotiated_capabilites = transceiver.getNegotiatedHeaderExtensions();
    353  const local_negotiated = transceiver.getNegotiatedHeaderExtensions().find(ext => {
    354    return ext.uri === selected_capability.uri;
    355  });
    356  assert_equals(local_negotiated.direction, 'stopped', 'in t1 Negotiated');
    357 }, 'A subsequent offer from the other side will not reoffer extensions not negotiated by the initial offerer');
    358 </script>