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