tor-browser

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

1674892.html (4638B)


      1 <!DOCTYPE html>
      2 <!--
      3 Verifies cross-graph ports are cleaned up when a MediaStreamTrack connected
      4 to MediaStreamAudioSourceNode is stopped.
      5 -->
      6 <html class="reftest-wait">
      7 <head>
      8  <title>Test bug 1674892 - MediaStreamAudioSourceNode with getUserMedia and different sample rate</title>
      9  <script>
     10    document.addEventListener("DOMContentLoaded", async () => {
     11      const iframe = document.createElement("iframe");
     12      iframe.srcdoc = `
     13        <!DOCTYPE html>
     14        <html>
     15        <head>
     16          <script>
     17            let SpecialStream = SpecialPowers.wrap(MediaStream);
     18            let ctx, stream, sourceNode, analyser, dataArray;
     19 
     20            window.addEventListener("message", async (event) => {
     21              if (event.data === "startAudio") {
     22                ctx = new AudioContext({ sampleRate: 32000 });
     23 
     24                stream = await navigator.mediaDevices.getUserMedia({
     25                  audio: true,
     26                  fake: true
     27                });
     28 
     29                sourceNode = ctx.createMediaStreamSource(stream);
     30                sourceNode.connect(ctx.destination);
     31 
     32                analyser = ctx.createAnalyser();
     33                analyser.fftSize = 256;
     34                dataArray = new Uint8Array(analyser.frequencyBinCount);
     35                sourceNode.connect(analyser);
     36 
     37                while ((analyser.getByteFrequencyData(dataArray), dataArray.every(v => v == 0))) {
     38                  await new Promise(r => requestAnimationFrame(r));
     39                }
     40 
     41                const count = await SpecialStream.countUnderlyingStreams();
     42                parent.postMessage({ type: "ready", value: count }, "*");
     43              } else if (event.data === "destroy") {
     44                // Clean up all resources
     45                if (stream) {
     46                  // Stopping the MediaStreamTrack breaks connection to
     47                  // CrossGraphTransmitter, which can cause issues if the
     48                  // transmitter attempts to read from that connection during
     49                  // ::ProcessInput before the CrossGraph pair is fully torn
     50                  // down.
     51                  stream.getTracks().forEach(track => track.stop());
     52                  stream = null;
     53                }
     54                if (analyser) {
     55                  analyser.disconnect();
     56                  analyser = null;
     57                }
     58                if (sourceNode) {
     59                  sourceNode.disconnect();
     60                  sourceNode = null;
     61                }
     62                if (ctx) {
     63                  await ctx.close();
     64                  ctx = null;
     65                }
     66                dataArray = null;
     67 
     68                // Force garbage collection
     69                await new Promise(r => SpecialPowers.exactGC(r));
     70 
     71                const count = await SpecialStream.countUnderlyingStreams();
     72                parent.postMessage({ type: "destroyed", value: count }, "*");
     73              }
     74            });
     75 
     76            // Notify parent that iframe is ready with initial stream count
     77            (async () => {
     78              const count = await SpecialStream.countUnderlyingStreams();
     79              parent.postMessage({ type: "init", value: count }, "*");
     80            })();
     81          <\/script>
     82        </head>
     83        <body></body>
     84        </html>
     85      `;
     86 
     87      document.body.appendChild(iframe);
     88 
     89      // Helper to wait for iframe messages
     90      const waitForMessage = (type) => {
     91        return new Promise(resolve => {
     92          const handler = (event) => {
     93            if (event.data.type === type) {
     94              window.removeEventListener("message", handler);
     95              resolve(event.data.value);
     96            }
     97          };
     98          window.addEventListener("message", handler);
     99        });
    100      };
    101 
    102      // Wait for iframe to initialize and get initial stream count
    103      const initialCount = await waitForMessage("init");
    104      dump(`Initial stream count: ${initialCount}\n`);
    105 
    106      // Start audio and wait for it to be ready
    107      iframe.contentWindow.postMessage("startAudio", "*");
    108      const readyCount = await waitForMessage("ready");
    109      dump(`Stream count when audio ready: ${readyCount}\n`);
    110 
    111      // Destroy all resources in the iframe while MediaStream is active
    112      iframe.contentWindow.postMessage("destroy", "*");
    113      const finalCount = await waitForMessage("destroyed");
    114      dump(`Final stream count: ${finalCount}\n`);
    115 
    116      // Check if streams were cleaned up
    117      if (finalCount === initialCount) {
    118        document.documentElement.removeAttribute("class");
    119      } else {
    120        dump("Streams were not cleaned up properly.\n");
    121      }
    122    });
    123  </script>
    124 </head>
    125 <body></body>
    126 </html>