tor-browser

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

key-conversion-exceptions.any.js (9058B)


      1 // META: global=window,worker
      2 // META: title=IndexedDB: Exceptions thrown during key conversion
      3 // META: script=resources/support.js
      4 // META: timeout=long
      5 
      6 'use strict';
      7 
      8 // Convenience function for tests that only need to run code in onupgradeneeded.
      9 function indexeddb_upgrade_only_test(upgrade_callback, description) {
     10  indexeddb_test(upgrade_callback, t => t.done(), description);
     11 }
     12 
     13 // Key that throws during conversion.
     14 function throwing_key(name) {
     15  const throws = [];
     16  throws.length = 1;
     17  const err = new Error('throwing from getter');
     18  err.name = name;
     19  Object.defineProperty(throws, '0', {
     20    get: function() {
     21      throw err;
     22    },
     23    enumerable: true,
     24  });
     25  return [throws, err];
     26 }
     27 
     28 const valid_key = [];
     29 const invalid_key = {};
     30 
     31 // Calls method on receiver with the specified number of args (default 1)
     32 // and asserts that the method fails appropriately (rethrowing if
     33 // conversion throws, or DataError if not a valid key), and that
     34 // the first argument is fully processed before the second argument
     35 // (if appropriate).
     36 function check_method(receiver, method, args) {
     37  args = args || 1;
     38  if (args < 2) {
     39    const [key, err] = throwing_key('getter');
     40    assert_throws_exactly(err, () => {
     41      receiver[method](key);
     42    }, 'key conversion with throwing getter should rethrow');
     43 
     44    assert_throws_dom('DataError', () => {
     45      receiver[method](invalid_key);
     46    }, 'key conversion with invalid key should throw DataError');
     47  } else {
     48    const [key1, err1] = throwing_key('getter 1');
     49    const [key2, err2] = throwing_key('getter 2');
     50    assert_throws_exactly(err1, () => {
     51      receiver[method](key1, key2);
     52    }, 'first key conversion with throwing getter should rethrow');
     53 
     54    assert_throws_dom('DataError', () => {
     55      receiver[method](invalid_key, key2);
     56    }, 'first key conversion with invalid key should throw DataError');
     57 
     58    assert_throws_exactly(err2, () => {
     59      receiver[method](valid_key, key2);
     60    }, 'second key conversion with throwing getter should rethrow');
     61 
     62    assert_throws_dom('DataError', () => {
     63      receiver[method](valid_key, invalid_key);
     64    }, 'second key conversion with invalid key should throw DataError');
     65  }
     66 }
     67 
     68 // Verifies that invalid keys throw when used with the `IDBGetAllOptions`
     69 // dictionary. `getAllRecords()` added `IDBGetAllOptions`, which `getAll()` and
     70 // `getAllKeys()` also support.
     71 function check_method_with_get_all_options(receiver, method) {
     72  assert_throws_dom('DataError', () => {
     73    receiver[method]({query: invalid_key});
     74  }, 'options query key conversion with invalid key should throw DataError');
     75 
     76  const [key, err] = throwing_key('getter');
     77  assert_throws_exactly(err, () => {
     78    receiver[method]({query: key});
     79  }, 'options query key conversion with throwing getter should rethrow');
     80 
     81  // Verify `getAll()` and `getAllKeys()` throw when given an invalid key range
     82  // directly without the options dictionary.  `getAllRecords()` only supports
     83  // the options dictionary.
     84  if (method !== 'getAllRecords') {
     85    assert_throws_exactly(err, () => {
     86      receiver[method](key);
     87    }, 'query key conversion with throwing getter should rethrow');
     88  }
     89 }
     90 
     91 // Static key comparison utility on IDBFactory.
     92 test(
     93    t => check_method(indexedDB, 'cmp', 2),
     94    'IDBFactory cmp() static with throwing/invalid keys');
     95 
     96 // Continue methods on IDBCursor.
     97 indexeddb_upgrade_only_test((t, db) => {
     98  const store = db.createObjectStore('store');
     99  store.put('a', 1).onerror = t.unreached_func('put should succeed');
    100 
    101  const request = store.openCursor();
    102  request.onerror = t.unreached_func('openCursor should succeed');
    103  request.onsuccess = t.step_func(() => {
    104    const cursor = request.result;
    105    assert_not_equals(cursor, null, 'cursor should find a value');
    106    check_method(cursor, 'continue');
    107  });
    108 }, 'IDBCursor continue() method with throwing/invalid keys');
    109 
    110 indexeddb_upgrade_only_test((t, db) => {
    111  const store = db.createObjectStore('store');
    112  const index = store.createIndex('index', 'prop');
    113  store.put({prop: 'a'}, 1).onerror = t.unreached_func('put should succeed');
    114 
    115  const request = index.openCursor();
    116  request.onerror = t.unreached_func('openCursor should succeed');
    117  request.onsuccess = t.step_func(() => {
    118    const cursor = request.result;
    119    assert_not_equals(cursor, null, 'cursor should find a value');
    120 
    121    check_method(cursor, 'continuePrimaryKey', 2);
    122  });
    123 }, null, 'IDBCursor continuePrimaryKey() method with throwing/invalid keys');
    124 
    125 // Mutation methods on IDBCursor.
    126 indexeddb_upgrade_only_test((t, db) => {
    127  const store = db.createObjectStore('store', {keyPath: 'prop'});
    128  store.put({prop: 1}).onerror = t.unreached_func('put should succeed');
    129 
    130  const request = store.openCursor();
    131  request.onerror = t.unreached_func('openCursor should succeed');
    132  request.onsuccess = t.step_func(() => {
    133    const cursor = request.result;
    134    assert_not_equals(cursor, null, 'cursor should find a value');
    135 
    136    const value = {};
    137    let err;
    138    [value.prop, err] = throwing_key('getter');
    139    assert_throws_exactly(err, () => {
    140      cursor.update(value);
    141    }, 'throwing getter should rethrow during clone');
    142 
    143    // Throwing from the getter during key conversion is
    144    // not possible since (1) a clone is used, (2) only own
    145    // properties are cloned, and (3) only own properties
    146    // are used for key path evaluation.
    147 
    148    value.prop = invalid_key;
    149    assert_throws_dom('DataError', () => {
    150      cursor.update(value);
    151    }, 'key conversion with invalid key should throw DataError');
    152  });
    153 }, 'IDBCursor update() method with throwing/invalid keys');
    154 
    155 // Static constructors on IDBKeyRange
    156 ['only', 'lowerBound', 'upperBound'].forEach((method) => {
    157  test(
    158      t => check_method(IDBKeyRange, method),
    159      'IDBKeyRange ' + method + '() static with throwing/invalid keys');
    160 });
    161 
    162 test(
    163    t => check_method(IDBKeyRange, 'bound', 2),
    164    'IDBKeyRange bound() static with throwing/invalid keys');
    165 
    166 // Insertion methods on IDBObjectStore.
    167 ['add', 'put'].forEach((method) => {
    168  indexeddb_upgrade_only_test((t, db) => {
    169    const out_of_line = db.createObjectStore('out-of-line keys');
    170    const in_line = db.createObjectStore('in-line keys', {keyPath: 'prop'});
    171    let [key, err] = throwing_key('getter');
    172    assert_throws_exactly(err, () => {
    173      out_of_line[method]('value', key);
    174    }, 'key conversion with throwing getter should rethrow');
    175 
    176    assert_throws_dom('DataError', () => {
    177      out_of_line[method]('value', invalid_key);
    178    }, 'key conversion with invalid key should throw DataError');
    179 
    180    const value = {};
    181    [value.prop, err] = throwing_key('getter');
    182    assert_throws_exactly(err, () => {
    183      in_line[method](value);
    184    }, 'throwing getter should rethrow during clone');
    185 
    186    // Throwing from the getter during key conversion is
    187    // not possible since (1) a clone is used, (2) only own
    188    // properties are cloned, and (3) only own properties
    189    // are used for key path evaluation.
    190 
    191    value.prop = invalid_key;
    192    assert_throws_dom('DataError', () => {
    193      in_line[method](value);
    194    }, 'key conversion with invalid key should throw DataError');
    195  }, `IDBObjectStore ${method}() method with throwing/invalid keys`);
    196 });
    197 
    198 // Generic (key-or-key-path) methods on IDBObjectStore.
    199 ['delete',
    200 'get',
    201 'getKey',
    202 'count',
    203 'openCursor',
    204 'openKeyCursor',
    205 ].forEach(method => {
    206  indexeddb_upgrade_only_test((t, db) => {
    207    const store = db.createObjectStore('store');
    208 
    209    check_method(store, method);
    210  }, `IDBObjectStore ${method}() method with throwing/invalid keys`);
    211 });
    212 
    213 // Generic (key-or-key-path) methods on IDBIndex.
    214 ['get',
    215 'getKey',
    216 'count',
    217 'openCursor',
    218 'openKeyCursor',
    219 ].forEach((method) => {
    220  indexeddb_upgrade_only_test((t, db) => {
    221    const store = db.createObjectStore('store');
    222    const index = store.createIndex('index', 'keyPath');
    223 
    224    check_method(index, method);
    225  }, `IDBIndex ${method}() method with throwing/invalid keys`);
    226 });
    227 
    228 // Verify methods that take `IDBGetAllOptions` on `IDBObjectStore`.
    229 ['getAll',
    230 'getAllKeys',
    231 'getAllRecords',
    232 ].forEach(method => {
    233  indexeddb_upgrade_only_test((t, db) => {
    234    const store = db.createObjectStore('store');
    235    if ('getAllRecords' in store) {
    236      check_method_with_get_all_options(store, method);
    237    } else if (method !== 'getAllRecords') {
    238      // This browser does not support `getAllRecords()` or the
    239      // `IDBGetAllOptions` dictionary.
    240      check_method(store, method);
    241    }
    242  }, `IDBObjectStore ${method}() method with throwing/invalid keys`);
    243 });
    244 
    245 // Verify methods that take `IDBGetAllOptions` on `IDBIndex`.
    246 ['getAll', 'getAllKeys', 'getAllRecords'].forEach(method => {
    247  indexeddb_upgrade_only_test((t, db) => {
    248    const store = db.createObjectStore('store');
    249    const index = store.createIndex('index', 'keyPath');
    250    if ('getAllRecords' in index) {
    251      check_method_with_get_all_options(index, method);
    252    } else if (method !== 'getAllRecords') {
    253      check_method(store, method);
    254    }
    255  }, `IDBIndex ${method}() method with throwing/invalid keys`);
    256 });