keypath-exceptions.any.js (10452B)
1 // META: global=window,worker 2 // META: title=IndexedDB: Exceptions in extracting keys from values (ES bindings) 3 // META: script=resources/support.js 4 5 // Spec: https://w3c.github.io/IndexedDB/#extract-key-from-value 6 7 'use strict'; 8 9 indexeddb_test( 10 (t, db) => { 11 db.createObjectStore('store', {autoIncrement: true, keyPath: 'a.b.c'}); 12 }, 13 (t, db) => { 14 const tx = db.transaction('store', 'readwrite', {durability: 'relaxed'}); 15 assert_throws_dom('DataError', () => { 16 tx.objectStore('store').put({a: {b: 'foo'}}); 17 }, 'Put should throw if key can not be inserted at key path location.'); 18 t.done(); 19 }, 20 'The last element of keypath is validated'); 21 22 const err = Error(); 23 err.name = 'getter'; 24 25 function throwingGetter() { 26 throw err; 27 } 28 29 indexeddb_test( 30 function(t, db) { 31 const o = {}; 32 Object.defineProperty( 33 o, 'throws', 34 {get: throwingGetter, enumerable: false, configurable: true}); 35 36 // Value should be cloned before key path is evaluated, 37 // and non-enumerable getter will be ignored. The clone 38 // will have no such property, so key path evaluation 39 // will fail. 40 const s1 = db.createObjectStore('s1', {keyPath: 'throws'}); 41 assert_throws_dom('DataError', () => { 42 s1.put(o); 43 }, 'Key path failing to resolve should throw'); 44 45 // Value should be cloned before key path is evaluated, 46 // and non-enumerable getter will be ignored. The clone 47 // will have no such property, so key path evaluation 48 // will fail. 49 const s2 = db.createObjectStore('s2', {keyPath: 'throws.x'}); 50 assert_throws_dom('DataError', () => { 51 s2.put(o); 52 }, 'Key path failing to resolve should throw'); 53 54 // Value should be cloned before key path is evaluated, 55 // and non-enumerable getter will be ignored. The clone 56 // will have no such property, so generated key can be 57 // inserted. 58 const s3 = 59 db.createObjectStore('s3', {keyPath: 'throws', autoIncrement: true}); 60 assert_class_string( 61 s3.put(o), 'IDBRequest', 62 'Key injectability test at throwing getter should succeed'); 63 64 // Value should be cloned before key path is evaluated, 65 // and non-enumerable getter will be ignored. The clone 66 // will have no such property, so intermediate object 67 // and generated key can be inserted. 68 const s4 = db.createObjectStore( 69 's4', {keyPath: 'throws.x', autoIncrement: true}); 70 assert_class_string( 71 s4.put(o), 'IDBRequest', 72 'Key injectability test past throwing getter should succeed'); 73 }, 74 (t, db) => { 75 t.done(); 76 }, 77 'Key path evaluation: Exceptions from non-enumerable getters'); 78 79 indexeddb_test( 80 function(t, db) { 81 const o = {}; 82 Object.defineProperty( 83 o, 'throws', 84 {get: throwingGetter, enumerable: true, configurable: true}); 85 86 // Value should be cloned before key path is evaluated, 87 // and enumerable getter will rethrow. 88 const s1 = db.createObjectStore('s1', {keyPath: 'throws'}); 89 assert_throws_exactly(err, () => { 90 s1.put(o); 91 }, 'Key path resolving to throwing getter rethrows'); 92 93 // Value should be cloned before key path is evaluated, 94 // and enumerable getter will rethrow. 95 const s2 = db.createObjectStore('s2', {keyPath: 'throws.x'}); 96 assert_throws_exactly(err, () => { 97 s2.put(o); 98 }, 'Key path resolving past throwing getter rethrows'); 99 100 // Value should be cloned before key path is evaluated, 101 // and enumerable getter will rethrow. 102 const s3 = 103 db.createObjectStore('s3', {keyPath: 'throws', autoIncrement: true}); 104 assert_throws_exactly(err, () => { 105 s3.put(o); 106 }, 'Key injectability test at throwing getter should rethrow'); 107 108 // Value should be cloned before key path is evaluated, 109 // and enumerable getter will rethrow. 110 const s4 = db.createObjectStore( 111 's4', {keyPath: 'throws.x', autoIncrement: true}); 112 assert_throws_exactly(err, () => { 113 s4.put(o); 114 }, 'Key injectability test past throwing getter should rethrow'); 115 }, 116 (t, db) => { 117 t.done(); 118 }, 119 'Key path evaluation: Exceptions from enumerable getters'); 120 121 indexeddb_test( 122 (t, db) => { 123 // Implemented as function wrapper to clean up 124 // immediately after use, otherwise it may 125 // interfere with the test harness. 126 function with_proto_getter(f) { 127 return function() { 128 Object.defineProperty( 129 Object.prototype, 'throws', 130 {get: throwingGetter, enumerable: false, configurable: true}); 131 try { 132 f(); 133 } finally { 134 delete Object.prototype['throws']; 135 } 136 }; 137 } 138 139 // Value should be cloned before key path is evaluated, 140 // and non-enumerable getter will be ignored. The clone 141 // will have no own property, so key path evaluation will 142 // fail and DataError should be thrown. 143 const s1 = db.createObjectStore('s1', {keyPath: 'throws'}); 144 assert_throws_dom( 145 'DataError', with_proto_getter(function() { 146 s1.put({}); 147 }), 148 'Key path resolving to no own property throws DataError'); 149 150 // Value should be cloned before key path is evaluated, 151 // and non-enumerable getter will be ignored. The clone 152 // will have no own property, so key path evaluation will 153 // fail and DataError should be thrown. 154 const s2 = db.createObjectStore('s2', {keyPath: 'throws.x'}); 155 assert_throws_dom( 156 'DataError', with_proto_getter(function() { 157 s2.put({}); 158 }), 159 'Key path resolving past no own property throws DataError'); 160 161 // Value should be cloned before key path is evaluated, 162 // and non-enumerable getter will be ignored. The clone 163 // will have no own property, so key path evaluation will 164 // fail and injection can succeed. 165 const s3 = 166 db.createObjectStore('s3', {keyPath: 'throws', autoIncrement: true}); 167 assert_equals( 168 s3.put({}).readyState, 'pending', 169 'put should not throw due to inherited property'); 170 171 // Value should be cloned before key path is evaluated, 172 // and non-enumerable getter will be ignored. The clone 173 // will have no own property, so key path evaluation will 174 // fail and injection can succeed. 175 const s4 = db.createObjectStore( 176 's4', {keyPath: 'throws.x', autoIncrement: true}); 177 assert_equals( 178 s4.put({}).readyState, 'pending', 179 'put should not throw due to inherited property'); 180 }, 181 (t, db) => { 182 t.done(); 183 }, 184 'Key path evaluation: Exceptions from non-enumerable getters on prototype'); 185 186 indexeddb_test( 187 (t, db) => { 188 // Implemented as function wrapper to clean up 189 // immediately after use, otherwise it may 190 // interfere with the test harness. 191 function with_proto_getter(f) { 192 return () => { 193 Object.defineProperty( 194 Object.prototype, 'throws', 195 {get: throwingGetter, enumerable: true, configurable: true}); 196 try { 197 f(); 198 } finally { 199 delete Object.prototype['throws']; 200 } 201 }; 202 } 203 204 // Value should be cloned before key path is evaluated. 205 // The clone will have no own property, so key path 206 // evaluation will fail and DataError should be thrown. 207 const s1 = db.createObjectStore('s1', {keyPath: 'throws'}); 208 assert_throws_dom( 209 'DataError', with_proto_getter(function() { 210 s1.put({}); 211 }), 212 'Key path resolving to no own property throws DataError'); 213 214 // Value should be cloned before key path is evaluated. 215 // The clone will have no own property, so key path 216 // evaluation will fail and DataError should be thrown. 217 const s2 = db.createObjectStore('s2', {keyPath: 'throws.x'}); 218 assert_throws_dom( 219 'DataError', with_proto_getter(function() { 220 s2.put({}); 221 }), 222 'Key path resolving past throwing getter rethrows'); 223 224 // Value should be cloned before key path is evaluated. 225 // The clone will have no own property, so key path 226 // evaluation will fail and injection can succeed. 227 let s3 = 228 db.createObjectStore('s3', {keyPath: 'throws', autoIncrement: true}); 229 assert_equals( 230 s3.put({}).readyState, 'pending', 231 'put should not throw due to inherited property'); 232 233 // Value should be cloned before key path is evaluated. 234 // The clone will have no own property, so key path 235 // evaluation will fail and injection can succeed. 236 let s4 = db.createObjectStore( 237 's4', {keyPath: 'throws.x', autoIncrement: true}); 238 assert_equals( 239 s4.put({}).readyState, 'pending', 240 'put should not throw due to inherited property'); 241 }, 242 (t, db) => { 243 t.done(); 244 }, 245 'Key path evaluation: Exceptions from enumerable getters on prototype'); 246 247 indexeddb_test( 248 (t, db) => { 249 const store = db.createObjectStore('store'); 250 store.createIndex('index', 'index0'); 251 }, 252 (t, db) => { 253 const tx = db.transaction('store', 'readwrite', {durability: 'relaxed'}); 254 255 const array = []; 256 array[99] = 1; 257 258 // Implemented as function wrapper to clean up 259 // immediately after use, otherwise it may 260 // interfere with the test harness. 261 let getter_called = 0; 262 function with_proto_getter(f) { 263 const prop = '50'; 264 Object.defineProperty(Object.prototype, prop, { 265 enumerable: true, 266 configurable: true, 267 get: () => { 268 ++getter_called; 269 return 'foo'; 270 } 271 }); 272 try { 273 return f(); 274 } finally { 275 delete Object.prototype[prop]; 276 } 277 } 278 279 const request = with_proto_getter( 280 () => tx.objectStore('store').put({index0: array}, 'key')); 281 request.onerror = t.unreached_func('put should not fail'); 282 request.onsuccess = t.step_func(function() { 283 assert_equals( 284 getter_called, 0, 'Prototype getter should not be called'); 285 t.done(); 286 }); 287 }, 288 'Array key conversion should not invoke prototype getters');