| 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; | 
|  | 19 | Object.defineProperty(throws, '0', {get: function() { | 
|  | 20 | var err = new Error('throwing from getter'); | 
|  | 21 | err.name = name; | 
|  | 22 | throw err; | 
|  | 23 | }, enumerable: true}); | 
|  | 24 | return throws; | 
|  | 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) { | 
|  | 38 | assert_throws({name:'getter'}, () => { | 
|  | 39 | receiver[method](throwing_key('getter')); | 
|  | 40 | }, 'key conversion with throwing getter should rethrow'); | 
|  | 41 |  | 
|  | 42 | assert_throws('DataError', () => { | 
|  | 43 | receiver[method](invalid_key); | 
|  | 44 | }, 'key conversion with invalid key should throw DataError'); | 
|  | 45 | } else { | 
|  | 46 | assert_throws({name:'getter 1'}, () => { | 
|  | 47 | receiver[method](throwing_key('getter 1'), throwing_key('getter 2')); | 
|  | 48 | }, 'first key conversion with throwing getter should rethrow'); | 
|  | 49 |  | 
|  | 50 | assert_throws('DataError', () => { | 
|  | 51 | receiver[method](invalid_key, throwing_key('getter 2')); | 
|  | 52 | }, 'first key conversion with invalid key should throw DataError'); | 
|  | 53 |  | 
|  | 54 | assert_throws({name:'getter 2'}, () => { | 
|  | 55 | receiver[method](valid_key, throwing_key('getter 2')); | 
|  | 56 | }, 'second key conversion with throwing getter should rethrow'); | 
|  | 57 |  | 
|  | 58 | assert_throws('DataError', () => { | 
|  | 59 | receiver[method](valid_key, invalid_key); | 
|  | 60 | }, 'second key conversion with invalid key should throw DataError'); | 
|  | 61 | } | 
|  | 62 | } | 
|  | 63 |  | 
|  | 64 | // Static key comparison utility on IDBFactory. | 
|  | 65 | test(t => { | 
|  | 66 | check_method(indexedDB, 'cmp', 2); | 
|  | 67 | }, 'IDBFactory cmp() static with throwing/invalid keys'); | 
|  | 68 |  | 
|  | 69 | // Continue methods on IDBCursor. | 
|  | 70 | indexeddb_upgrade_only_test((t, db) => { | 
|  | 71 | var store = db.createObjectStore('store'); | 
|  | 72 | store.put('a', 1).onerror = t.unreached_func('put should succeed'); | 
|  | 73 |  | 
|  | 74 | var request = store.openCursor(); | 
|  | 75 | request.onerror = t.unreached_func('openCursor should succeed'); | 
|  | 76 | request.onsuccess = t.step_func(() => { | 
|  | 77 | var cursor = request.result; | 
|  | 78 | assert_not_equals(cursor, null, 'cursor should find a value'); | 
|  | 79 | check_method(cursor, 'continue'); | 
|  | 80 | }); | 
|  | 81 | }, 'IDBCursor continue() method with throwing/invalid keys'); | 
|  | 82 |  | 
|  | 83 | indexeddb_upgrade_only_test((t, db) => { | 
|  | 84 | var store = db.createObjectStore('store'); | 
|  | 85 | var index = store.createIndex('index', 'prop'); | 
|  | 86 | store.put({prop: 'a'}, 1).onerror = t.unreached_func('put should succeed'); | 
|  | 87 |  | 
|  | 88 | var request = index.openCursor(); | 
|  | 89 | request.onerror = t.unreached_func('openCursor should succeed'); | 
|  | 90 | request.onsuccess = t.step_func(() => { | 
|  | 91 | var cursor = request.result; | 
|  | 92 | assert_not_equals(cursor, null, 'cursor should find a value'); | 
|  | 93 |  | 
|  | 94 | check_method(cursor, 'continuePrimaryKey', 2); | 
|  | 95 | }); | 
|  | 96 | }, null, 'IDBCursor continuePrimaryKey() method with throwing/invalid keys'); | 
|  | 97 |  | 
|  | 98 | // Mutation methods on IDBCursor. | 
|  | 99 | indexeddb_upgrade_only_test((t, db) => { | 
|  | 100 | var store = db.createObjectStore('store', {keyPath: 'prop'}); | 
|  | 101 | store.put({prop: 1}).onerror = t.unreached_func('put should succeed'); | 
|  | 102 |  | 
|  | 103 | var request = store.openCursor(); | 
|  | 104 | request.onerror = t.unreached_func('openCursor should succeed'); | 
|  | 105 | request.onsuccess = t.step_func(() => { | 
|  | 106 | var cursor = request.result; | 
|  | 107 | assert_not_equals(cursor, null, 'cursor should find a value'); | 
|  | 108 |  | 
|  | 109 | var value = {}; | 
|  | 110 | value.prop = throwing_key('getter'); | 
|  | 111 | assert_throws({name: 'getter'}, () => { | 
|  | 112 | cursor.update(value); | 
|  | 113 | }, 'throwing getter should rethrow during clone'); | 
|  | 114 |  | 
|  | 115 | // Throwing from the getter during key conversion is | 
|  | 116 | // not possible since (1) a clone is used, (2) only own | 
|  | 117 | // properties are cloned, and (3) only own properties | 
|  | 118 | // are used for key path evaluation. | 
|  | 119 |  | 
|  | 120 | value.prop = invalid_key; | 
|  | 121 | assert_throws('DataError', () => { | 
|  | 122 | cursor.update(value); | 
|  | 123 | }, 'key conversion with invalid key should throw DataError'); | 
|  | 124 | }); | 
|  | 125 | }, 'IDBCursor update() method with throwing/invalid keys'); | 
|  | 126 |  | 
|  | 127 | // Static constructors on IDBKeyRange | 
|  | 128 | ['only', 'lowerBound', 'upperBound'].forEach(method => { | 
|  | 129 | test(t => { | 
|  | 130 | check_method(IDBKeyRange, method); | 
|  | 131 | }, 'IDBKeyRange ' + method + '() static with throwing/invalid keys'); | 
|  | 132 | }); | 
|  | 133 |  | 
|  | 134 | test(t => { | 
|  | 135 | check_method(IDBKeyRange, 'bound', 2); | 
|  | 136 | }, 'IDBKeyRange bound() static with throwing/invalid keys'); | 
|  | 137 |  | 
|  | 138 | // Insertion methods on IDBObjectStore. | 
|  | 139 | ['add', 'put'].forEach(method => { | 
|  | 140 | indexeddb_upgrade_only_test((t, db) => { | 
|  | 141 | var out_of_line = db.createObjectStore('out-of-line keys'); | 
|  | 142 | var in_line = db.createObjectStore('in-line keys', {keyPath: 'prop'}); | 
|  | 143 |  | 
|  | 144 | assert_throws({name:'getter'}, () => { | 
|  | 145 | out_of_line[method]('value', throwing_key('getter')); | 
|  | 146 | }, 'key conversion with throwing getter should rethrow'); | 
|  | 147 |  | 
|  | 148 | assert_throws('DataError', () => { | 
|  | 149 | out_of_line[method]('value', invalid_key); | 
|  | 150 | }, 'key conversion with invalid key should throw DataError'); | 
|  | 151 |  | 
|  | 152 | var value = {}; | 
|  | 153 | value.prop = throwing_key('getter'); | 
|  | 154 | assert_throws({name:'getter'}, () => { | 
|  | 155 | in_line[method](value); | 
|  | 156 | }, 'throwing getter should rethrow during clone'); | 
|  | 157 |  | 
|  | 158 | // Throwing from the getter during key conversion is | 
|  | 159 | // not possible since (1) a clone is used, (2) only own | 
|  | 160 | // properties are cloned, and (3) only own properties | 
|  | 161 | // are used for key path evaluation. | 
|  | 162 |  | 
|  | 163 | value.prop = invalid_key; | 
|  | 164 | assert_throws('DataError', () => { | 
|  | 165 | in_line[method](value); | 
|  | 166 | }, 'key conversion with invalid key should throw DataError'); | 
|  | 167 | }, `IDBObjectStore ${method}() method with throwing/invalid keys`); | 
|  | 168 | }); | 
|  | 169 |  | 
|  | 170 | // Generic (key-or-key-path) methods on IDBObjectStore. | 
|  | 171 | [ | 
|  | 172 | 'delete', 'get', 'getKey', 'getAll', 'getAllKeys', 'count', 'openCursor', | 
|  | 173 | 'openKeyCursor' | 
|  | 174 | ].forEach(method => { | 
|  | 175 | indexeddb_upgrade_only_test((t, db) => { | 
|  | 176 | var store = db.createObjectStore('store'); | 
|  | 177 |  | 
|  | 178 | check_method(store, method); | 
|  | 179 | }, `IDBObjectStore ${method}() method with throwing/invalid keys`); | 
|  | 180 | }); | 
|  | 181 |  | 
|  | 182 | // Generic (key-or-key-path) methods on IDBIndex. | 
|  | 183 | [ | 
|  | 184 | 'get', 'getKey', 'getAll', 'getAllKeys', 'count', 'openCursor', | 
|  | 185 | 'openKeyCursor' | 
|  | 186 | ].forEach(method => { | 
|  | 187 | indexeddb_upgrade_only_test((t, db) => { | 
|  | 188 | var store = db.createObjectStore('store'); | 
|  | 189 | var index = store.createIndex('index', 'keyPath'); | 
|  | 190 |  | 
|  | 191 | check_method(index, method); | 
|  | 192 | }, `IDBIndex ${method}() method with throwing/invalid keys`); | 
|  | 193 | }); | 
|  | 194 |  | 
|  | 195 | </script> |