tor-browser

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

generic-sensor-helpers.js (5838B)


      1 'use strict';
      2 
      3 // If two doubles differ by less than this amount, we can consider them
      4 // to be effectively equal.
      5 const kEpsilon = 1e-8;
      6 
      7 class RingBuffer {
      8  constructor(data) {
      9    if (!Array.isArray(data)) {
     10      throw new TypeError('`data` must be an array.');
     11    }
     12 
     13    this.bufferPosition_ = 0;
     14    this.data_ = Array.from(data);
     15  }
     16 
     17  get data() {
     18    return Array.from(this.data_);
     19  }
     20 
     21  next() {
     22    const value = this.data_[this.bufferPosition_];
     23    this.bufferPosition_ = (this.bufferPosition_ + 1) % this.data_.length;
     24    return {done: false, value: value};
     25  }
     26 
     27  value() {
     28    return this.data_[this.bufferPosition_];
     29  }
     30 
     31  [Symbol.iterator]() {
     32    return this;
     33  }
     34 
     35  reset() {
     36    this.bufferPosition_ = 0;
     37  }
     38 };
     39 
     40 // Calls test_driver.update_virtual_sensor() until it results in a "reading"
     41 // event. It waits |timeoutInMs| before considering that an event has not been
     42 // delivered.
     43 async function update_virtual_sensor_until_reading(
     44    t, readings, readingPromise, testDriverName, timeoutInMs) {
     45  while (true) {
     46    await test_driver.update_virtual_sensor(
     47        testDriverName, readings.next().value);
     48    const value = await Promise.race([
     49      new Promise(
     50          resolve => {t.step_timeout(() => resolve('TIMEOUT'), timeoutInMs)}),
     51      readingPromise,
     52    ]);
     53    if (value !== 'TIMEOUT') {
     54      break;
     55    }
     56  }
     57 }
     58 
     59 // This could be turned into a t.step_wait() call once
     60 // https://github.com/web-platform-tests/wpt/pull/34289 is merged.
     61 async function wait_for_virtual_sensor_state(testDriverName, predicate) {
     62  const result =
     63      await test_driver.get_virtual_sensor_information(testDriverName);
     64  if (!predicate(result)) {
     65    await wait_for_virtual_sensor_state(testDriverName, predicate);
     66  }
     67 }
     68 
     69 function validate_sensor_data(sensorData) {
     70  if (!('sensorName' in sensorData)) {
     71    throw new TypeError('sensorData.sensorName is missing');
     72  }
     73  if (!('permissionName' in sensorData)) {
     74    throw new TypeError('sensorData.permissionName is missing');
     75  }
     76  if (!('testDriverName' in sensorData)) {
     77    throw new TypeError('sensorData.testDriverName is missing');
     78  }
     79  if (sensorData.featurePolicyNames !== undefined &&
     80      !Array.isArray(sensorData.featurePolicyNames)) {
     81    throw new TypeError('sensorData.featurePolicyNames must be an array');
     82  }
     83 }
     84 
     85 function validate_reading_data(readingData) {
     86  if (!Array.isArray(readingData.readings)) {
     87    throw new TypeError('readingData.readings must be an array.');
     88  }
     89  if (!Array.isArray(readingData.expectedReadings)) {
     90    throw new TypeError('readingData.expectedReadings must be an array.');
     91  }
     92  if (readingData.readings.length < readingData.expectedReadings.length) {
     93    throw new TypeError(
     94        'readingData.readings\' length must be bigger than ' +
     95        'or equal to readingData.expectedReadings\' length.');
     96  }
     97  if (readingData.expectedRemappedReadings &&
     98      !Array.isArray(readingData.expectedRemappedReadings)) {
     99    throw new TypeError(
    100        'readingData.expectedRemappedReadings must be an ' +
    101        'array.');
    102  }
    103  if (readingData.expectedRemappedReadings &&
    104      readingData.expectedReadings.length !=
    105          readingData.expectedRemappedReadings.length) {
    106    throw new TypeError(
    107        'readingData.expectedReadings and ' +
    108        'readingData.expectedRemappedReadings must have the same ' +
    109        'length.');
    110  }
    111 }
    112 
    113 function get_sensor_reading_properties(sensor) {
    114  const className = sensor[Symbol.toStringTag];
    115  if ([
    116        'Accelerometer', 'GravitySensor', 'Gyroscope',
    117        'LinearAccelerationSensor', 'Magnetometer', 'ProximitySensor'
    118      ].includes(className)) {
    119    return ['x', 'y', 'z'];
    120  } else if (className == 'AmbientLightSensor') {
    121    return ['illuminance'];
    122  } else if ([
    123               'AbsoluteOrientationSensor', 'RelativeOrientationSensor'
    124             ].includes(className)) {
    125    return ['quaternion'];
    126  } else {
    127    throw new TypeError(`Unexpected sensor '${className}'`);
    128  }
    129 }
    130 
    131 // Checks that `sensor` and `expectedSensorLike` have the same properties
    132 // (except for timestamp) and they have the same values.
    133 //
    134 // Options allows configuring some aspects of the comparison:
    135 // - ignoreTimestamps (boolean): If true, `sensor` and `expectedSensorLike`'s
    136 //   "timestamp" attribute will not be compared. If `expectedSensorLike` does
    137 //   not have a "timestamp" attribute, the values will not be compared either.
    138 //   This is particularly useful when comparing sensor objects from different
    139 //   origins (and consequently different time origins).
    140 function assert_sensor_reading_equals(
    141    sensor, expectedSensorLike, options = {}) {
    142  for (const prop of get_sensor_reading_properties(sensor)) {
    143    assert_true(
    144        prop in expectedSensorLike,
    145        `expectedSensorLike must have a property called '${prop}'`);
    146    if (Array.isArray(sensor[prop]))
    147      assert_array_approx_equals(
    148          sensor[prop], expectedSensorLike[prop], kEpsilon);
    149    else
    150      assert_approx_equals(sensor[prop], expectedSensorLike[prop], kEpsilon);
    151  }
    152  assert_not_equals(sensor.timestamp, null);
    153 
    154  if ('timestamp' in expectedSensorLike && !options.ignoreTimestamps) {
    155    assert_equals(
    156        sensor.timestamp, expectedSensorLike.timestamp,
    157        'Sensor timestamps must be equal');
    158  }
    159 }
    160 
    161 function assert_sensor_reading_is_null(sensor) {
    162  for (const prop of get_sensor_reading_properties(sensor)) {
    163    assert_equals(sensor[prop], null);
    164  }
    165  assert_equals(sensor.timestamp, null);
    166 }
    167 
    168 function serialize_sensor_data(sensor) {
    169  const sensorData = {};
    170  for (const property of get_sensor_reading_properties(sensor)) {
    171    sensorData[property] = sensor[property];
    172  }
    173  sensorData['timestamp'] = sensor.timestamp;
    174 
    175  // Note that this is not serialized by postMessage().
    176  sensorData[Symbol.toStringTag] = sensor[Symbol.toStringTag];
    177 
    178  return sensorData;
    179 }