tor-browser

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

blacksilence.js (3989B)


      1 (function (global) {
      2  "use strict";
      3 
      4  // an invertible check on the condition.
      5  // if the constraint is applied, then the check is direct
      6  // if not applied, then the result should be reversed
      7  function check(constraintApplied, condition, message) {
      8    var good = constraintApplied ? condition : !condition;
      9    message =
     10      (constraintApplied ? "with" : "without") +
     11      " constraint: should " +
     12      (constraintApplied ? "" : "not ") +
     13      message +
     14      " = " +
     15      (good ? "OK" : "waiting...");
     16    info(message);
     17    return good;
     18  }
     19 
     20  function mkElement(type) {
     21    // This makes an unattached element.
     22    // It's not rendered to save the cycles that costs on b2g emulator
     23    // and it gets dropped (and GC'd) when the test is done.
     24    var e = document.createElement(type);
     25    e.width = 32;
     26    e.height = 24;
     27    document.getElementById("display").appendChild(e);
     28    return e;
     29  }
     30 
     31  // Runs checkFunc until it reports success.
     32  // This is kludgy, but you have to wait for media to start flowing, and it
     33  // can't be any old media, it has to include real data, for which we have no
     34  // reliable signals to use as a trigger.
     35  function periodicCheck(checkFunc) {
     36    var resolve;
     37    var done = false;
     38    // This returns a function so that we create 10 closures in the loop, not
     39    // one; and so that the timers don't all start straight away
     40    var waitAndCheck = counter => () => {
     41      if (done) {
     42        return Promise.resolve();
     43      }
     44      return new Promise(r => setTimeout(r, 200 << counter)).then(() => {
     45        if (checkFunc()) {
     46          done = true;
     47          resolve();
     48        }
     49      });
     50    };
     51 
     52    var chain = Promise.resolve();
     53    for (var i = 0; i < 10; ++i) {
     54      chain = chain.then(waitAndCheck(i));
     55    }
     56    return new Promise(r => (resolve = r));
     57  }
     58 
     59  function isSilence(audioData) {
     60    var silence = true;
     61    for (var i = 0; i < audioData.length; ++i) {
     62      if (audioData[i] !== 128) {
     63        silence = false;
     64      }
     65    }
     66    return silence;
     67  }
     68 
     69  function checkAudio(constraintApplied, stream) {
     70    var audio = mkElement("audio");
     71    audio.srcObject = stream;
     72    audio.play();
     73 
     74    var context = new AudioContext();
     75    var source = context.createMediaStreamSource(stream);
     76    var analyser = context.createAnalyser();
     77    source.connect(analyser);
     78    analyser.connect(context.destination);
     79 
     80    return periodicCheck(() => {
     81      var sampleCount = analyser.frequencyBinCount;
     82      info("got some audio samples: " + sampleCount);
     83      var buffer = new Uint8Array(sampleCount);
     84      analyser.getByteTimeDomainData(buffer);
     85 
     86      var silent = check(
     87        constraintApplied,
     88        isSilence(buffer),
     89        "be silence for audio"
     90      );
     91      return sampleCount > 0 && silent;
     92    }).then(() => {
     93      source.disconnect();
     94      analyser.disconnect();
     95      audio.pause();
     96      ok(true, "audio is " + (constraintApplied ? "" : "not ") + "silent");
     97    });
     98  }
     99 
    100  function checkVideo(constraintApplied, stream) {
    101    var video = mkElement("video");
    102    video.srcObject = stream;
    103    video.play();
    104 
    105    return periodicCheck(() => {
    106      try {
    107        var canvas = mkElement("canvas");
    108        var ctx = canvas.getContext("2d");
    109        // Have to guard drawImage with the try as well, due to bug 879717. If
    110        // we get an error, this round fails, but that failure is usually just
    111        // transitory.
    112        ctx.drawImage(video, 0, 0);
    113        ctx.getImageData(0, 0, 1, 1);
    114        return check(
    115          constraintApplied,
    116          false,
    117          "throw on getImageData for video"
    118        );
    119      } catch (e) {
    120        return check(
    121          constraintApplied,
    122          e.name === "SecurityError",
    123          "get a security error: " + e.name
    124        );
    125      }
    126    }).then(() => {
    127      video.pause();
    128      ok(true, "video is " + (constraintApplied ? "" : "not ") + "protected");
    129    });
    130  }
    131 
    132  global.audioIsSilence = checkAudio;
    133  global.videoIsBlack = checkVideo;
    134 })(this);