tor-browser

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

RTCDTMFSender-ontonechange.https.html (9928B)


      1 <!doctype html>
      2 <meta charset=utf-8>
      3 <title>RTCDTMFSender.prototype.ontonechange</title>
      4 <meta name="timeout" content="long">
      5 <script src="/resources/testharness.js"></script>
      6 <script src="/resources/testharnessreport.js"></script>
      7 <script src="RTCPeerConnection-helper.js"></script>
      8 <script src="RTCDTMFSender-helper.js"></script>
      9 <script>
     10  'use strict';
     11 
     12  // Test is based on the following editor draft:
     13  // https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.html
     14 
     15  // The following helper functions are called from RTCPeerConnection-helper.js
     16  //   generateAnswer
     17 
     18  // The following helper functions are called from RTCDTMFSender-helper.js
     19  //   test_tone_change_events
     20  //   getTransceiver
     21 
     22  /*
     23    7.  Peer-to-peer DTMF
     24      partial interface RTCRtpSender {
     25        readonly attribute RTCDTMFSender? dtmf;
     26      };
     27 
     28      interface RTCDTMFSender : EventTarget {
     29        void insertDTMF(DOMString tones,
     30                        optional unsigned long duration = 100,
     31                        optional unsigned long interToneGap = 70);
     32                 attribute EventHandler ontonechange;
     33        readonly attribute DOMString    toneBuffer;
     34      };
     35 
     36      [Constructor(DOMString type, RTCDTMFToneChangeEventInit eventInitDict)]
     37      interface RTCDTMFToneChangeEvent : Event {
     38        readonly attribute DOMString tone;
     39      };
     40   */
     41 
     42  /*
     43    7.2.  insertDTMF
     44      11. If a Playout task is scheduled to be run; abort these steps; otherwise queue
     45          a task that runs the following steps (Playout task):
     46        3.  If toneBuffer is an empty string, fire an event named tonechange with an
     47            empty string at the RTCDTMFSender object and abort these steps.
     48        4.  Remove the first character from toneBuffer and let that character be tone.
     49        6.  Queue a task to be executed in duration + interToneGap ms from now that
     50            runs the steps labelled Playout task.
     51        7.  Fire an event named tonechange with a string consisting of tone at the
     52            RTCDTMFSender object.
     53   */
     54  test_tone_change_events((t, dtmfSender) => {
     55    dtmfSender.insertDTMF('123');
     56  }, [
     57    ['1', '23', 0],
     58    ['2', '3', 170],
     59    ['3', '', 170],
     60    ['', '', 170]
     61  ], 'insertDTMF() with default duration and intertoneGap should fire tonechange events at the expected time');
     62 
     63  test_tone_change_events((t, dtmfSender) => {
     64    dtmfSender.insertDTMF('abc', 100, 70);
     65  }, [
     66    ['A', 'BC', 0],
     67    ['B', 'C', 170],
     68    ['C', '', 170],
     69    ['', '', 170]
     70  ], 'insertDTMF() with explicit duration and intertoneGap should fire tonechange events at the expected time');
     71 
     72  /*
     73    7.2.  insertDTMF
     74      10. If toneBuffer is an empty string, abort these steps.
     75   */
     76  async_test(t => {
     77    createDtmfSender()
     78    .then(dtmfSender => {
     79      dtmfSender.addEventListener('tonechange',
     80        t.unreached_func('Expect no tonechange event to be fired'));
     81 
     82      dtmfSender.insertDTMF('', 100, 70);
     83 
     84      t.step_timeout(t.step_func_done(), 300);
     85    })
     86    .catch(t.step_func(err => {
     87      assert_unreached(`Unexpected promise rejection: ${err}`);
     88    }));
     89  }, `insertDTMF('') should not fire any tonechange event, including for '' tone`);
     90 
     91  /*
     92    7.2.  insertDTMF
     93      8. If the value of the duration parameter is less than 40, set it to 40.
     94         If, on the other hand, the value is greater than 6000, set it to 6000.
     95   */
     96  test_tone_change_events((t, dtmfSender) => {
     97      dtmfSender.insertDTMF('ABC', 10, 70);
     98  }, [
     99    ['A', 'BC', 0],
    100    ['B', 'C', 110],
    101    ['C', '', 110],
    102    ['', '', 110]
    103  ], 'insertDTMF() with duration less than 40 should be clamped to 40');
    104 
    105  /*
    106    7.2.  insertDTMF
    107      9. If the value of the interToneGap parameter is less than 30, set it to 30.
    108   */
    109  test_tone_change_events((t, dtmfSender) => {
    110    dtmfSender.insertDTMF('ABC', 100, 10);
    111  }, [
    112    ['A', 'BC', 0],
    113    ['B', 'C', 130],
    114    ['C', '', 130],
    115    ['', '', 130]
    116  ],
    117  'insertDTMF() with interToneGap less than 30 should be clamped to 30');
    118 
    119  /*
    120    [w3c/webrtc-pc#1373]
    121    This step is added to handle the "," character correctly. "," supposed to delay the next
    122    tonechange event by 2000ms.
    123 
    124    7.2.  insertDTMF
    125      11.5. If tone is "," delay sending tones for 2000 ms on the associated RTP media
    126            stream, and queue a task to be executed in 2000 ms from now that runs the
    127            steps labelled Playout task.
    128   */
    129  test_tone_change_events((t, dtmfSender) => {
    130    dtmfSender.insertDTMF('A,B', 100, 70);
    131 
    132  }, [
    133    ['A', ',B', 0],
    134    [',', 'B', 170],
    135    ['B', '', 2000],
    136    ['', '', 170]
    137  ], 'insertDTMF with comma should delay next tonechange event for a constant 2000ms');
    138 
    139  /*
    140    7.2.  insertDTMF
    141      11.1. If transceiver.stopped is true, abort these steps.
    142   */
    143  test_tone_change_events((t, dtmfSender, pc) => {
    144    const transceiver = getTransceiver(pc);
    145    dtmfSender.addEventListener('tonechange', ev => {
    146      if(ev.tone === 'B') {
    147        transceiver.stop();
    148      }
    149    });
    150 
    151    dtmfSender.insertDTMF('ABC', 100, 70);
    152  }, [
    153    ['A', 'BC', 0],
    154    ['B', 'C', 170]
    155  ], 'insertDTMF() with transceiver stopped in the middle should stop future tonechange events from firing');
    156 
    157  /*
    158    7.2.  insertDTMF
    159      3.  If a Playout task is scheduled to be run, abort these steps;
    160          otherwise queue a task that runs the following steps (Playout task):
    161   */
    162  test_tone_change_events((t, dtmfSender) => {
    163    dtmfSender.addEventListener('tonechange', ev => {
    164      if(ev.tone === 'B') {
    165        dtmfSender.insertDTMF('12', 100, 70);
    166      }
    167    });
    168 
    169    dtmfSender.insertDTMF('ABC', 100, 70);
    170  }, [
    171    ['A', 'BC', 0],
    172    ['B', 'C', 170],
    173    ['1', '2', 170],
    174    ['2', '', 170],
    175    ['', '', 170]
    176  ], 'Calling insertDTMF() in the middle of tonechange events should cause future tonechanges to be updated to new tones');
    177 
    178 
    179  /*
    180    7.2.  insertDTMF
    181      3.  If a Playout task is scheduled to be run, abort these steps;
    182          otherwise queue a task that runs the following steps (Playout task):
    183   */
    184  test_tone_change_events((t, dtmfSender) => {
    185    dtmfSender.addEventListener('tonechange', ev => {
    186      if(ev.tone === 'B') {
    187        dtmfSender.insertDTMF('12', 100, 70);
    188        dtmfSender.insertDTMF('34', 100, 70);
    189      }
    190    });
    191 
    192    dtmfSender.insertDTMF('ABC', 100, 70);
    193  }, [
    194    ['A', 'BC', 0],
    195    ['B', 'C', 170],
    196    ['3', '4', 170],
    197    ['4', '', 170],
    198    ['', '', 170]
    199  ], 'Calling insertDTMF() multiple times in the middle of tonechange events should cause future tonechanges to be updated the last provided tones');
    200 
    201  /*
    202    7.2.  insertDTMF
    203      3.  If a Playout task is scheduled to be run, abort these steps;
    204          otherwise queue a task that runs the following steps (Playout task):
    205   */
    206  test_tone_change_events((t, dtmfSender) => {
    207    dtmfSender.addEventListener('tonechange', ev => {
    208      if(ev.tone === 'B') {
    209        dtmfSender.insertDTMF('');
    210      }
    211    });
    212 
    213    dtmfSender.insertDTMF('ABC', 100, 70);
    214  }, [
    215    ['A', 'BC', 0],
    216    ['B', 'C', 170],
    217    ['', '', 170]
    218  ], `Calling insertDTMF('') in the middle of tonechange events should stop future tonechange events from firing`);
    219 
    220  /*
    221    7.2.  insertDTMF
    222      11.2.  If transceiver.currentDirection is recvonly or inactive, abort these steps.
    223   */
    224  promise_test(async t => {
    225    const pc = new RTCPeerConnection();
    226    t.add_cleanup(() => pc.close());
    227    const dtmfSender = await createDtmfSender(pc);
    228    const pc2 = pc.otherPc;
    229    assert_true(pc2 instanceof RTCPeerConnection,
    230      'Expect pc2 to be a RTCPeerConnection');
    231    t.add_cleanup(() => pc2.close());
    232    const transceiver = pc.getTransceivers()[0];
    233    assert_equals(transceiver.sender.dtmf, dtmfSender);
    234 
    235    // Since setRemoteDescription happens in parallel with tonechange event,
    236    // We use a flag and allow tonechange events to be fired as long as
    237    // the promise returned by setRemoteDescription is not yet resolved.
    238    let remoteDescriptionIsSet = false;
    239 
    240    // We only do basic tone verification and not check timing here
    241    let expectedTones = ['A', 'B', 'C', 'D', ''];
    242 
    243    const firstTone = new Promise(resolve => {
    244      const onToneChange = t.step_func(ev => {
    245        assert_false(remoteDescriptionIsSet,
    246          'Expect no tonechange event to be fired after currentDirection is changed to recvonly');
    247 
    248        const { tone } = ev;
    249        const expectedTone = expectedTones.shift();
    250        assert_equals(tone, expectedTone,
    251          `Expect fired event.tone to be ${expectedTone}`);
    252 
    253        if(tone === 'A') {
    254          resolve();
    255        }
    256      });
    257      dtmfSender.addEventListener('tonechange', onToneChange);
    258    });
    259 
    260    dtmfSender.insertDTMF('ABCD', 100, 70);
    261    await firstTone;
    262 
    263    // Only change transceiver.direction after the first
    264    // tonechange event, to make sure that tonechange is triggered
    265    // then stopped
    266    transceiver.direction = 'recvonly';
    267    await exchangeOfferAnswer(pc, pc2);
    268    assert_equals(transceiver.currentDirection, 'inactive');
    269    remoteDescriptionIsSet = true;
    270 
    271    await new Promise(resolve => t.step_timeout(resolve, 300));
    272  }, `Setting transceiver.currentDirection to recvonly in the middle of tonechange events should stop future tonechange events from firing`);
    273 
    274  /* Section 7.3 - Tone change event */
    275  test(t => {
    276    let ev = new RTCDTMFToneChangeEvent('tonechange', {'tone': '1'});
    277    assert_equals(ev.type, 'tonechange');
    278    assert_equals(ev.tone, '1');
    279  }, 'Tone change event constructor works');
    280 
    281  test(t => {
    282    let ev = new RTCDTMFToneChangeEvent('worngname', {});
    283  }, 'Tone change event with unexpected name should not crash');
    284 
    285  test(t => {
    286    const ev1 = new RTCDTMFToneChangeEvent('tonechange', {});
    287    assert_equals(ev1.tone, '');
    288 
    289    assert_equals(RTCDTMFToneChangeEvent.constructor.length, 1);
    290    const ev2 = new RTCDTMFToneChangeEvent('tonechange');
    291    assert_equals(ev2.tone, '');
    292  }, 'Tone change event init optional parameters');
    293 
    294 </script>