tor-browser

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

utils.js (7817B)


      1 function make_audio_data(timestamp, channels, sampleRate, frames) {
      2  let data = new Float32Array(frames*channels);
      3 
      4  // This generates samples in a planar format.
      5  for (var channel = 0; channel < channels; channel++) {
      6    let hz = 100 + channel * 50; // sound frequency
      7    let base_index = channel * frames;
      8    for (var i = 0; i < frames; i++) {
      9      let t = (i / sampleRate) * hz * (Math.PI * 2);
     10      data[base_index + i] = Math.sin(t);
     11    }
     12  }
     13 
     14  return new AudioData({
     15    timestamp: timestamp,
     16    data: data,
     17    numberOfChannels: channels,
     18    numberOfFrames: frames,
     19    sampleRate: sampleRate,
     20    format: "f32-planar",
     21  });
     22 }
     23 
     24 function makeOffscreenCanvas(width, height, options) {
     25  let canvas = new OffscreenCanvas(width, height);
     26  let ctx = canvas.getContext('2d', options);
     27  ctx.fillStyle = 'rgba(50, 100, 150, 255)';
     28  ctx.fillRect(0, 0, width, height);
     29  return canvas;
     30 }
     31 
     32 function makeImageBitmap(width, height) {
     33  return makeOffscreenCanvas(width, height).transferToImageBitmap();
     34 }
     35 
     36 // Gives a chance to pending output and error callbacks to complete before
     37 // resolving.
     38 function endAfterEventLoopTurn() {
     39  return new Promise(resolve => step_timeout(resolve, 0));
     40 }
     41 
     42 // Returns a codec initialization with callbacks that expected to not be called.
     43 function getDefaultCodecInit(test) {
     44  return {
     45    output: test.unreached_func("unexpected output"),
     46    error: test.unreached_func("unexpected error"),
     47  }
     48 }
     49 
     50 // Checks that codec can be configured, reset, reconfigured, and that incomplete
     51 // or invalid configs throw errors immediately.
     52 function testConfigurations(codec, validConfig, unsupportedCodecsList) {
     53  assert_equals(codec.state, "unconfigured");
     54 
     55  const requiredConfigPairs = validConfig;
     56  let incrementalConfig = {};
     57 
     58  for (let key in requiredConfigPairs) {
     59    // Configure should fail while required keys are missing.
     60    assert_throws_js(TypeError, () => { codec.configure(incrementalConfig); });
     61    incrementalConfig[key] = requiredConfigPairs[key];
     62    assert_equals(codec.state, "unconfigured");
     63  }
     64 
     65  // Configure should pass once incrementalConfig meets all requirements.
     66  codec.configure(incrementalConfig);
     67  assert_equals(codec.state, "configured");
     68 
     69  // We should be able to reconfigure the codec.
     70  codec.configure(incrementalConfig);
     71  assert_equals(codec.state, "configured");
     72 
     73  let config = incrementalConfig;
     74 
     75  unsupportedCodecsList.forEach(unsupportedCodec => {
     76    // Invalid codecs should fail.
     77    config.codec = unsupportedCodec;
     78    assert_throws_dom('NotSupportedError', () => {
     79      codec.configure(config);
     80    }, unsupportedCodec);
     81  });
     82 
     83  // The failed configures should not affect the current config.
     84  assert_equals(codec.state, "configured");
     85 
     86  // Test we can configure after a reset.
     87  codec.reset()
     88  assert_equals(codec.state, "unconfigured");
     89 
     90  codec.configure(validConfig);
     91  assert_equals(codec.state, "configured");
     92 }
     93 
     94 // Performs an encode or decode with the provided input, depending on whether
     95 // the passed codec is an encoder or a decoder.
     96 function encodeOrDecodeShouldThrow(codec, input) {
     97  // We are testing encode/decode on codecs in invalid states.
     98  assert_not_equals(codec.state, "configured");
     99 
    100  if (codec.decode) {
    101    assert_throws_dom("InvalidStateError",
    102                      () => codec.decode(input),
    103                      "decode");
    104  } else if (codec.encode) {
    105    // Encoders consume frames, so clone it to be safe.
    106    assert_throws_dom("InvalidStateError",
    107                  () => codec.encode(input.clone()),
    108                  "encode");
    109 
    110  } else {
    111    assert_unreached("Codec should have encode or decode function");
    112  }
    113 }
    114 
    115 // Makes sure that we cannot close, configure, reset, flush, decode or encode a
    116 // closed codec.
    117 function testClosedCodec(test, codec, validconfig, codecInput) {
    118  assert_equals(codec.state, "unconfigured");
    119 
    120  codec.close();
    121  assert_equals(codec.state, "closed");
    122 
    123  assert_throws_dom("InvalidStateError",
    124                    () => codec.configure(validconfig),
    125                    "configure");
    126  assert_throws_dom("InvalidStateError",
    127                    () => codec.reset(),
    128                    "reset");
    129  assert_throws_dom("InvalidStateError",
    130                    () => codec.close(),
    131                    "close");
    132 
    133  encodeOrDecodeShouldThrow(codec, codecInput);
    134 
    135  return promise_rejects_dom(test, 'InvalidStateError', codec.flush(), 'flush');
    136 }
    137 
    138 // Makes sure we cannot flush, encode or decode with an unconfigured coded, and
    139 // that reset is a valid no-op.
    140 function testUnconfiguredCodec(test, codec, codecInput) {
    141  assert_equals(codec.state, "unconfigured");
    142 
    143  // Configure() and Close() are valid operations that would transition us into
    144  // a different state.
    145 
    146  // Resetting an unconfigured encoder is a no-op.
    147  codec.reset();
    148  assert_equals(codec.state, "unconfigured");
    149 
    150  encodeOrDecodeShouldThrow(codec, codecInput);
    151 
    152  return promise_rejects_dom(test, 'InvalidStateError', codec.flush(), 'flush');
    153 }
    154 
    155 // Reference values generated by:
    156 // https://fiddle.skia.org/c/f100d4d5f085a9e09896aabcbc463868
    157 
    158 const kSRGBPixel = [50, 100, 150, 255];
    159 const kP3Pixel = [62, 99, 146, 255];
    160 const kRec2020Pixel = [87, 106, 151, 255];
    161 
    162 const kCanvasOptionsP3Uint8 = {
    163  colorSpace: 'display-p3',
    164  pixelFormat: 'uint8'
    165 };
    166 
    167 const kImageSettingOptionsP3Uint8 = {
    168  colorSpace: 'display-p3',
    169  storageFormat: 'uint8'
    170 };
    171 
    172 const kCanvasOptionsRec2020Uint8 = {
    173  colorSpace: 'rec2020',
    174  pixelFormat: 'uint8'
    175 };
    176 
    177 const kImageSettingOptionsRec2020Uint8 = {
    178  colorSpace: 'rec2020',
    179  storageFormat: 'uint8'
    180 };
    181 
    182 function testCanvas(ctx, width, height, expected_pixel, imageSetting, assert_compares) {
    183  // The dup getImageData is to workaournd crbug.com/1100233
    184  let imageData = ctx.getImageData(0, 0, width, height, imageSetting);
    185  let colorData = ctx.getImageData(0, 0, width, height, imageSetting).data;
    186  const kMaxPixelToCheck = 128 * 96;
    187  let step = width * height / kMaxPixelToCheck;
    188  step = Math.round(step);
    189  step = (step < 1) ? 1 : step;
    190  for (let i = 0; i < 4 * width * height; i += (4 * step)) {
    191    assert_compares(colorData[i], expected_pixel[0]);
    192    assert_compares(colorData[i + 1], expected_pixel[1]);
    193    assert_compares(colorData[i + 2], expected_pixel[2]);
    194    assert_compares(colorData[i + 3], expected_pixel[3]);
    195  }
    196 }
    197 
    198 function makeDetachedArrayBuffer() {
    199  const buffer = new ArrayBuffer(10);
    200  const view = new Uint8Array(buffer);
    201  new MessageChannel().port1.postMessage(buffer, [buffer]);
    202  return view;
    203 }
    204 
    205 function isFrameClosed(frame) {
    206  return frame.format == null && frame.codedWidth == 0 &&
    207         frame.codedHeight == 0 && frame.displayWidth == 0 &&
    208         frame.displayHeight == 0 && frame.codedRect == null &&
    209         frame.visibleRect == null;
    210 }
    211 
    212 function testImageBitmapToAndFromVideoFrame(
    213    width, height, expectedPixel, canvasOptions, imageBitmapOptions,
    214    imageSetting) {
    215  let canvas = new OffscreenCanvas(width, height);
    216  let ctx = canvas.getContext('2d', canvasOptions);
    217  ctx.fillStyle = 'rgb(50, 100, 150)';
    218  ctx.fillRect(0, 0, width, height);
    219  testCanvas(ctx, width, height, expectedPixel, imageSetting, assert_equals);
    220 
    221  return createImageBitmap(canvas, imageBitmapOptions)
    222      .then((fromImageBitmap) => {
    223        let videoFrame = new VideoFrame(fromImageBitmap, {timestamp: 0});
    224        return createImageBitmap(videoFrame, imageBitmapOptions);
    225      })
    226      .then((toImageBitmap) => {
    227        let myCanvas = new OffscreenCanvas(width, height);
    228        let myCtx = myCanvas.getContext('2d', canvasOptions);
    229        myCtx.drawImage(toImageBitmap, 0, 0);
    230        let tolerance = 2;
    231        testCanvas(
    232            myCtx, width, height, expectedPixel, imageSetting,
    233            (actual, expected) => {
    234              assert_approx_equals(actual, expected, tolerance);
    235            });
    236      });
    237 }