| 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> |