| pwnall | de422a8 | 2017-02-08 01:30:14 | [diff] [blame] | 1 | <!doctype html> |
| 2 | <meta charset=utf-8> |
| 3 | <title>IndexedDB: Exceptions thrown during key conversion</title> |
| James Graham | 6d984d2 | 2017-03-08 13:12:22 | [diff] [blame] | 4 | <meta name=timeout content=long> |
| pwnall | de422a8 | 2017-02-08 01:30:14 | [diff] [blame] | 5 | <script src="/resources/testharness.js"></script> |
| 6 | <script src="/resources/testharnessreport.js"></script> |
| 7 | <script src="support.js"></script> |
| 8 | <script> |
| 9 | |
| 10 | // Convenience function for tests that only need to run code in onupgradeneeded. |
| 11 | function indexeddb_upgrade_only_test(upgrade_callback, description) { |
| 12 | indexeddb_test(upgrade_callback, t => { t.done(); }, description); |
| 13 | } |
| 14 | |
| 15 | // Key that throws during conversion. |
| 16 | function throwing_key(name) { |
| 17 | var throws = []; |
| 18 | throws.length = 1; |
| Boris Zbarsky | f294a12 | 2020-01-24 00:24:00 | [diff] [blame] | 19 | const err = new Error('throwing from getter'); |
| 20 | err.name = name; |
| pwnall | de422a8 | 2017-02-08 01:30:14 | [diff] [blame] | 21 | Object.defineProperty(throws, '0', {get: function() { |
| pwnall | de422a8 | 2017-02-08 01:30:14 | [diff] [blame] | 22 | throw err; |
| 23 | }, enumerable: true}); |
| Boris Zbarsky | f294a12 | 2020-01-24 00:24:00 | [diff] [blame] | 24 | return [throws, err]; |
| pwnall | de422a8 | 2017-02-08 01:30:14 | [diff] [blame] | 25 | } |
| 26 | |
| 27 | var valid_key = []; |
| 28 | var invalid_key = {}; |
| 29 | |
| 30 | // Calls method on receiver with the specified number of args (default 1) |
| 31 | // and asserts that the method fails appropriately (rethrowing if |
| 32 | // conversion throws, or DataError if not a valid key), and that |
| 33 | // the first argument is fully processed before the second argument |
| 34 | // (if appropriate). |
| 35 | function check_method(receiver, method, args) { |
| 36 | args = args || 1; |
| 37 | if (args < 2) { |
| Boris Zbarsky | f294a12 | 2020-01-24 00:24:00 | [diff] [blame] | 38 | const [key, err] = throwing_key('getter'); |
| 39 | assert_throws_exactly(err, () => { |
| 40 | receiver[method](key); |
| pwnall | de422a8 | 2017-02-08 01:30:14 | [diff] [blame] | 41 | }, 'key conversion with throwing getter should rethrow'); |
| 42 | |
| Stephen McGruer | d510304 | 2020-01-23 21:45:45 | [diff] [blame] | 43 | assert_throws_dom('DataError', () => { |
| pwnall | de422a8 | 2017-02-08 01:30:14 | [diff] [blame] | 44 | receiver[method](invalid_key); |
| 45 | }, 'key conversion with invalid key should throw DataError'); |
| 46 | } else { |
| Boris Zbarsky | f294a12 | 2020-01-24 00:24:00 | [diff] [blame] | 47 | const [key1, err1] = throwing_key('getter 1'); |
| 48 | const [key2, err2] = throwing_key('getter 2'); |
| 49 | assert_throws_exactly(err1, () => { |
| 50 | receiver[method](key1, key2); |
| pwnall | de422a8 | 2017-02-08 01:30:14 | [diff] [blame] | 51 | }, 'first key conversion with throwing getter should rethrow'); |
| 52 | |
| Stephen McGruer | d510304 | 2020-01-23 21:45:45 | [diff] [blame] | 53 | assert_throws_dom('DataError', () => { |
| Boris Zbarsky | f294a12 | 2020-01-24 00:24:00 | [diff] [blame] | 54 | receiver[method](invalid_key, key2); |
| pwnall | de422a8 | 2017-02-08 01:30:14 | [diff] [blame] | 55 | }, 'first key conversion with invalid key should throw DataError'); |
| 56 | |
| Boris Zbarsky | f294a12 | 2020-01-24 00:24:00 | [diff] [blame] | 57 | assert_throws_exactly(err2, () => { |
| 58 | receiver[method](valid_key, key2); |
| pwnall | de422a8 | 2017-02-08 01:30:14 | [diff] [blame] | 59 | }, 'second key conversion with throwing getter should rethrow'); |
| 60 | |
| Stephen McGruer | d510304 | 2020-01-23 21:45:45 | [diff] [blame] | 61 | assert_throws_dom('DataError', () => { |
| pwnall | de422a8 | 2017-02-08 01:30:14 | [diff] [blame] | 62 | receiver[method](valid_key, invalid_key); |
| 63 | }, 'second key conversion with invalid key should throw DataError'); |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | // Static key comparison utility on IDBFactory. |
| 68 | test(t => { |
| 69 | check_method(indexedDB, 'cmp', 2); |
| 70 | }, 'IDBFactory cmp() static with throwing/invalid keys'); |
| 71 | |
| 72 | // Continue methods on IDBCursor. |
| 73 | indexeddb_upgrade_only_test((t, db) => { |
| 74 | var store = db.createObjectStore('store'); |
| 75 | store.put('a', 1).onerror = t.unreached_func('put should succeed'); |
| 76 | |
| 77 | var request = store.openCursor(); |
| 78 | request.onerror = t.unreached_func('openCursor should succeed'); |
| 79 | request.onsuccess = t.step_func(() => { |
| 80 | var cursor = request.result; |
| 81 | assert_not_equals(cursor, null, 'cursor should find a value'); |
| 82 | check_method(cursor, 'continue'); |
| 83 | }); |
| 84 | }, 'IDBCursor continue() method with throwing/invalid keys'); |
| 85 | |
| 86 | indexeddb_upgrade_only_test((t, db) => { |
| 87 | var store = db.createObjectStore('store'); |
| 88 | var index = store.createIndex('index', 'prop'); |
| 89 | store.put({prop: 'a'}, 1).onerror = t.unreached_func('put should succeed'); |
| 90 | |
| 91 | var request = index.openCursor(); |
| 92 | request.onerror = t.unreached_func('openCursor should succeed'); |
| 93 | request.onsuccess = t.step_func(() => { |
| 94 | var cursor = request.result; |
| 95 | assert_not_equals(cursor, null, 'cursor should find a value'); |
| 96 | |
| 97 | check_method(cursor, 'continuePrimaryKey', 2); |
| 98 | }); |
| 99 | }, null, 'IDBCursor continuePrimaryKey() method with throwing/invalid keys'); |
| 100 | |
| 101 | // Mutation methods on IDBCursor. |
| 102 | indexeddb_upgrade_only_test((t, db) => { |
| 103 | var store = db.createObjectStore('store', {keyPath: 'prop'}); |
| 104 | store.put({prop: 1}).onerror = t.unreached_func('put should succeed'); |
| 105 | |
| 106 | var request = store.openCursor(); |
| 107 | request.onerror = t.unreached_func('openCursor should succeed'); |
| 108 | request.onsuccess = t.step_func(() => { |
| 109 | var cursor = request.result; |
| 110 | assert_not_equals(cursor, null, 'cursor should find a value'); |
| 111 | |
| 112 | var value = {}; |
| Boris Zbarsky | f294a12 | 2020-01-24 00:24:00 | [diff] [blame] | 113 | var err; |
| 114 | [value.prop, err] = throwing_key('getter'); |
| 115 | assert_throws_exactly(err, () => { |
| pwnall | de422a8 | 2017-02-08 01:30:14 | [diff] [blame] | 116 | cursor.update(value); |
| 117 | }, 'throwing getter should rethrow during clone'); |
| 118 | |
| 119 | // Throwing from the getter during key conversion is |
| 120 | // not possible since (1) a clone is used, (2) only own |
| 121 | // properties are cloned, and (3) only own properties |
| 122 | // are used for key path evaluation. |
| 123 | |
| 124 | value.prop = invalid_key; |
| Stephen McGruer | d510304 | 2020-01-23 21:45:45 | [diff] [blame] | 125 | assert_throws_dom('DataError', () => { |
| pwnall | de422a8 | 2017-02-08 01:30:14 | [diff] [blame] | 126 | cursor.update(value); |
| 127 | }, 'key conversion with invalid key should throw DataError'); |
| 128 | }); |
| 129 | }, 'IDBCursor update() method with throwing/invalid keys'); |
| 130 | |
| 131 | // Static constructors on IDBKeyRange |
| 132 | ['only', 'lowerBound', 'upperBound'].forEach(method => { |
| 133 | test(t => { |
| 134 | check_method(IDBKeyRange, method); |
| 135 | }, 'IDBKeyRange ' + method + '() static with throwing/invalid keys'); |
| 136 | }); |
| 137 | |
| 138 | test(t => { |
| 139 | check_method(IDBKeyRange, 'bound', 2); |
| 140 | }, 'IDBKeyRange bound() static with throwing/invalid keys'); |
| 141 | |
| 142 | // Insertion methods on IDBObjectStore. |
| 143 | ['add', 'put'].forEach(method => { |
| 144 | indexeddb_upgrade_only_test((t, db) => { |
| 145 | var out_of_line = db.createObjectStore('out-of-line keys'); |
| 146 | var in_line = db.createObjectStore('in-line keys', {keyPath: 'prop'}); |
| Boris Zbarsky | f294a12 | 2020-01-24 00:24:00 | [diff] [blame] | 147 | var [key, err] = throwing_key('getter'); |
| 148 | assert_throws_exactly(err, () => { |
| 149 | out_of_line[method]('value', key); |
| pwnall | de422a8 | 2017-02-08 01:30:14 | [diff] [blame] | 150 | }, 'key conversion with throwing getter should rethrow'); |
| 151 | |
| Stephen McGruer | d510304 | 2020-01-23 21:45:45 | [diff] [blame] | 152 | assert_throws_dom('DataError', () => { |
| pwnall | de422a8 | 2017-02-08 01:30:14 | [diff] [blame] | 153 | out_of_line[method]('value', invalid_key); |
| 154 | }, 'key conversion with invalid key should throw DataError'); |
| 155 | |
| 156 | var value = {}; |
| Boris Zbarsky | f294a12 | 2020-01-24 00:24:00 | [diff] [blame] | 157 | [value.prop, err] = throwing_key('getter'); |
| 158 | assert_throws_exactly(err, () => { |
| pwnall | de422a8 | 2017-02-08 01:30:14 | [diff] [blame] | 159 | in_line[method](value); |
| 160 | }, 'throwing getter should rethrow during clone'); |
| 161 | |
| 162 | // Throwing from the getter during key conversion is |
| 163 | // not possible since (1) a clone is used, (2) only own |
| 164 | // properties are cloned, and (3) only own properties |
| 165 | // are used for key path evaluation. |
| 166 | |
| 167 | value.prop = invalid_key; |
| Stephen McGruer | d510304 | 2020-01-23 21:45:45 | [diff] [blame] | 168 | assert_throws_dom('DataError', () => { |
| pwnall | de422a8 | 2017-02-08 01:30:14 | [diff] [blame] | 169 | in_line[method](value); |
| 170 | }, 'key conversion with invalid key should throw DataError'); |
| 171 | }, `IDBObjectStore ${method}() method with throwing/invalid keys`); |
| 172 | }); |
| 173 | |
| 174 | // Generic (key-or-key-path) methods on IDBObjectStore. |
| 175 | [ |
| 176 | 'delete', 'get', 'getKey', 'getAll', 'getAllKeys', 'count', 'openCursor', |
| 177 | 'openKeyCursor' |
| 178 | ].forEach(method => { |
| 179 | indexeddb_upgrade_only_test((t, db) => { |
| 180 | var store = db.createObjectStore('store'); |
| 181 | |
| 182 | check_method(store, method); |
| 183 | }, `IDBObjectStore ${method}() method with throwing/invalid keys`); |
| 184 | }); |
| 185 | |
| 186 | // Generic (key-or-key-path) methods on IDBIndex. |
| 187 | [ |
| 188 | 'get', 'getKey', 'getAll', 'getAllKeys', 'count', 'openCursor', |
| 189 | 'openKeyCursor' |
| 190 | ].forEach(method => { |
| 191 | indexeddb_upgrade_only_test((t, db) => { |
| 192 | var store = db.createObjectStore('store'); |
| 193 | var index = store.createIndex('index', 'keyPath'); |
| 194 | |
| 195 | check_method(index, method); |
| 196 | }, `IDBIndex ${method}() method with throwing/invalid keys`); |
| 197 | }); |
| 198 | |
| 199 | </script> |