288 lines
10 KiB
JavaScript
288 lines
10 KiB
JavaScript
// META: global=window,worker
|
|
// META: title=IndexedDB: Exceptions in extracting keys from values (ES bindings)
|
|
// META: script=resources/support.js
|
|
|
|
// Spec: https://w3c.github.io/IndexedDB/#extract-key-from-value
|
|
|
|
'use strict';
|
|
|
|
indexeddb_test(
|
|
(t, db) => {
|
|
db.createObjectStore('store', {autoIncrement: true, keyPath: 'a.b.c'});
|
|
},
|
|
(t, db) => {
|
|
const tx = db.transaction('store', 'readwrite', {durability: 'relaxed'});
|
|
assert_throws_dom('DataError', () => {
|
|
tx.objectStore('store').put({a: {b: 'foo'}});
|
|
}, 'Put should throw if key can not be inserted at key path location.');
|
|
t.done();
|
|
},
|
|
'The last element of keypath is validated');
|
|
|
|
const err = Error();
|
|
err.name = 'getter';
|
|
|
|
function throwingGetter() {
|
|
throw err;
|
|
}
|
|
|
|
indexeddb_test(
|
|
function(t, db) {
|
|
const o = {};
|
|
Object.defineProperty(
|
|
o, 'throws',
|
|
{get: throwingGetter, enumerable: false, configurable: true});
|
|
|
|
// Value should be cloned before key path is evaluated,
|
|
// and non-enumerable getter will be ignored. The clone
|
|
// will have no such property, so key path evaluation
|
|
// will fail.
|
|
const s1 = db.createObjectStore('s1', {keyPath: 'throws'});
|
|
assert_throws_dom('DataError', () => {
|
|
s1.put(o);
|
|
}, 'Key path failing to resolve should throw');
|
|
|
|
// Value should be cloned before key path is evaluated,
|
|
// and non-enumerable getter will be ignored. The clone
|
|
// will have no such property, so key path evaluation
|
|
// will fail.
|
|
const s2 = db.createObjectStore('s2', {keyPath: 'throws.x'});
|
|
assert_throws_dom('DataError', () => {
|
|
s2.put(o);
|
|
}, 'Key path failing to resolve should throw');
|
|
|
|
// Value should be cloned before key path is evaluated,
|
|
// and non-enumerable getter will be ignored. The clone
|
|
// will have no such property, so generated key can be
|
|
// inserted.
|
|
const s3 =
|
|
db.createObjectStore('s3', {keyPath: 'throws', autoIncrement: true});
|
|
assert_class_string(
|
|
s3.put(o), 'IDBRequest',
|
|
'Key injectability test at throwing getter should succeed');
|
|
|
|
// Value should be cloned before key path is evaluated,
|
|
// and non-enumerable getter will be ignored. The clone
|
|
// will have no such property, so intermediate object
|
|
// and generated key can be inserted.
|
|
const s4 = db.createObjectStore(
|
|
's4', {keyPath: 'throws.x', autoIncrement: true});
|
|
assert_class_string(
|
|
s4.put(o), 'IDBRequest',
|
|
'Key injectability test past throwing getter should succeed');
|
|
},
|
|
(t, db) => {
|
|
t.done();
|
|
},
|
|
'Key path evaluation: Exceptions from non-enumerable getters');
|
|
|
|
indexeddb_test(
|
|
function(t, db) {
|
|
const o = {};
|
|
Object.defineProperty(
|
|
o, 'throws',
|
|
{get: throwingGetter, enumerable: true, configurable: true});
|
|
|
|
// Value should be cloned before key path is evaluated,
|
|
// and enumerable getter will rethrow.
|
|
const s1 = db.createObjectStore('s1', {keyPath: 'throws'});
|
|
assert_throws_exactly(err, () => {
|
|
s1.put(o);
|
|
}, 'Key path resolving to throwing getter rethrows');
|
|
|
|
// Value should be cloned before key path is evaluated,
|
|
// and enumerable getter will rethrow.
|
|
const s2 = db.createObjectStore('s2', {keyPath: 'throws.x'});
|
|
assert_throws_exactly(err, () => {
|
|
s2.put(o);
|
|
}, 'Key path resolving past throwing getter rethrows');
|
|
|
|
// Value should be cloned before key path is evaluated,
|
|
// and enumerable getter will rethrow.
|
|
const s3 =
|
|
db.createObjectStore('s3', {keyPath: 'throws', autoIncrement: true});
|
|
assert_throws_exactly(err, () => {
|
|
s3.put(o);
|
|
}, 'Key injectability test at throwing getter should rethrow');
|
|
|
|
// Value should be cloned before key path is evaluated,
|
|
// and enumerable getter will rethrow.
|
|
const s4 = db.createObjectStore(
|
|
's4', {keyPath: 'throws.x', autoIncrement: true});
|
|
assert_throws_exactly(err, () => {
|
|
s4.put(o);
|
|
}, 'Key injectability test past throwing getter should rethrow');
|
|
},
|
|
(t, db) => {
|
|
t.done();
|
|
},
|
|
'Key path evaluation: Exceptions from enumerable getters');
|
|
|
|
indexeddb_test(
|
|
(t, db) => {
|
|
// Implemented as function wrapper to clean up
|
|
// immediately after use, otherwise it may
|
|
// interfere with the test harness.
|
|
function with_proto_getter(f) {
|
|
return function() {
|
|
Object.defineProperty(
|
|
Object.prototype, 'throws',
|
|
{get: throwingGetter, enumerable: false, configurable: true});
|
|
try {
|
|
f();
|
|
} finally {
|
|
delete Object.prototype['throws'];
|
|
}
|
|
};
|
|
}
|
|
|
|
// Value should be cloned before key path is evaluated,
|
|
// and non-enumerable getter will be ignored. The clone
|
|
// will have no own property, so key path evaluation will
|
|
// fail and DataError should be thrown.
|
|
const s1 = db.createObjectStore('s1', {keyPath: 'throws'});
|
|
assert_throws_dom(
|
|
'DataError', with_proto_getter(function() {
|
|
s1.put({});
|
|
}),
|
|
'Key path resolving to no own property throws DataError');
|
|
|
|
// Value should be cloned before key path is evaluated,
|
|
// and non-enumerable getter will be ignored. The clone
|
|
// will have no own property, so key path evaluation will
|
|
// fail and DataError should be thrown.
|
|
const s2 = db.createObjectStore('s2', {keyPath: 'throws.x'});
|
|
assert_throws_dom(
|
|
'DataError', with_proto_getter(function() {
|
|
s2.put({});
|
|
}),
|
|
'Key path resolving past no own property throws DataError');
|
|
|
|
// Value should be cloned before key path is evaluated,
|
|
// and non-enumerable getter will be ignored. The clone
|
|
// will have no own property, so key path evaluation will
|
|
// fail and injection can succeed.
|
|
const s3 =
|
|
db.createObjectStore('s3', {keyPath: 'throws', autoIncrement: true});
|
|
assert_equals(
|
|
s3.put({}).readyState, 'pending',
|
|
'put should not throw due to inherited property');
|
|
|
|
// Value should be cloned before key path is evaluated,
|
|
// and non-enumerable getter will be ignored. The clone
|
|
// will have no own property, so key path evaluation will
|
|
// fail and injection can succeed.
|
|
const s4 = db.createObjectStore(
|
|
's4', {keyPath: 'throws.x', autoIncrement: true});
|
|
assert_equals(
|
|
s4.put({}).readyState, 'pending',
|
|
'put should not throw due to inherited property');
|
|
},
|
|
(t, db) => {
|
|
t.done();
|
|
},
|
|
'Key path evaluation: Exceptions from non-enumerable getters on prototype');
|
|
|
|
indexeddb_test(
|
|
(t, db) => {
|
|
// Implemented as function wrapper to clean up
|
|
// immediately after use, otherwise it may
|
|
// interfere with the test harness.
|
|
function with_proto_getter(f) {
|
|
return () => {
|
|
Object.defineProperty(
|
|
Object.prototype, 'throws',
|
|
{get: throwingGetter, enumerable: true, configurable: true});
|
|
try {
|
|
f();
|
|
} finally {
|
|
delete Object.prototype['throws'];
|
|
}
|
|
};
|
|
}
|
|
|
|
// Value should be cloned before key path is evaluated.
|
|
// The clone will have no own property, so key path
|
|
// evaluation will fail and DataError should be thrown.
|
|
const s1 = db.createObjectStore('s1', {keyPath: 'throws'});
|
|
assert_throws_dom(
|
|
'DataError', with_proto_getter(function() {
|
|
s1.put({});
|
|
}),
|
|
'Key path resolving to no own property throws DataError');
|
|
|
|
// Value should be cloned before key path is evaluated.
|
|
// The clone will have no own property, so key path
|
|
// evaluation will fail and DataError should be thrown.
|
|
const s2 = db.createObjectStore('s2', {keyPath: 'throws.x'});
|
|
assert_throws_dom(
|
|
'DataError', with_proto_getter(function() {
|
|
s2.put({});
|
|
}),
|
|
'Key path resolving past throwing getter rethrows');
|
|
|
|
// Value should be cloned before key path is evaluated.
|
|
// The clone will have no own property, so key path
|
|
// evaluation will fail and injection can succeed.
|
|
let s3 =
|
|
db.createObjectStore('s3', {keyPath: 'throws', autoIncrement: true});
|
|
assert_equals(
|
|
s3.put({}).readyState, 'pending',
|
|
'put should not throw due to inherited property');
|
|
|
|
// Value should be cloned before key path is evaluated.
|
|
// The clone will have no own property, so key path
|
|
// evaluation will fail and injection can succeed.
|
|
let s4 = db.createObjectStore(
|
|
's4', {keyPath: 'throws.x', autoIncrement: true});
|
|
assert_equals(
|
|
s4.put({}).readyState, 'pending',
|
|
'put should not throw due to inherited property');
|
|
},
|
|
(t, db) => {
|
|
t.done();
|
|
},
|
|
'Key path evaluation: Exceptions from enumerable getters on prototype');
|
|
|
|
indexeddb_test(
|
|
(t, db) => {
|
|
const store = db.createObjectStore('store');
|
|
store.createIndex('index', 'index0');
|
|
},
|
|
(t, db) => {
|
|
const tx = db.transaction('store', 'readwrite', {durability: 'relaxed'});
|
|
|
|
const array = [];
|
|
array[99] = 1;
|
|
|
|
// Implemented as function wrapper to clean up
|
|
// immediately after use, otherwise it may
|
|
// interfere with the test harness.
|
|
let getter_called = 0;
|
|
function with_proto_getter(f) {
|
|
const prop = '50';
|
|
Object.defineProperty(Object.prototype, prop, {
|
|
enumerable: true,
|
|
configurable: true,
|
|
get: () => {
|
|
++getter_called;
|
|
return 'foo';
|
|
}
|
|
});
|
|
try {
|
|
return f();
|
|
} finally {
|
|
delete Object.prototype[prop];
|
|
}
|
|
}
|
|
|
|
const request = with_proto_getter(
|
|
() => tx.objectStore('store').put({index0: array}, 'key'));
|
|
request.onerror = t.unreached_func('put should not fail');
|
|
request.onsuccess = t.step_func(function() {
|
|
assert_equals(
|
|
getter_called, 0, 'Prototype getter should not be called');
|
|
t.done();
|
|
});
|
|
},
|
|
'Array key conversion should not invoke prototype getters');
|