Bug 1118028 - Allow objectStores and indexes to be renamed. r=khuey

--HG--
extra : rebase_source : 4f2d1da2ddafbe786c3908eb46723b4d2f0c48cc
This commit is contained in:
Bevis Tseng 2016-03-30 11:04:56 +08:00
parent d73d8b0f4e
commit e0e7ead1f0
23 changed files with 1325 additions and 6 deletions

View File

@ -3987,7 +3987,7 @@ UpgradeIndexDataValuesFunction::OnFunctionCall(mozIStorageValueArray* aArguments
rv = MakeCompressedIndexDataValues(oldIdv, newIdv, &newIdvLength);
std::pair<uint8_t*, int> data(newIdv.release(), newIdvLength);
nsCOMPtr<nsIVariant> result = new storage::AdoptedBlobVariant(data);
result.forget(aResult);
@ -6990,6 +6990,10 @@ private:
virtual bool
RecvDeleteObjectStore(const int64_t& aObjectStoreId) override;
virtual bool
RecvRenameObjectStore(const int64_t& aObjectStoreId,
const nsString& aName) override;
virtual bool
RecvCreateIndex(const int64_t& aObjectStoreId,
const IndexMetadata& aMetadata) override;
@ -6998,6 +7002,11 @@ private:
RecvDeleteIndex(const int64_t& aObjectStoreId,
const int64_t& aIndexId) override;
virtual bool
RecvRenameIndex(const int64_t& aObjectStoreId,
const int64_t& aIndexId,
const nsString& aName) override;
virtual PBackgroundIDBRequestParent*
AllocPBackgroundIDBRequestParent(const RequestParams& aParams) override;
@ -7747,6 +7756,30 @@ private:
DoDatabaseWork(DatabaseConnection* aConnection) override;
};
class RenameObjectStoreOp final
: public VersionChangeTransactionOp
{
friend class VersionChangeTransaction;
const RefPtr<FullObjectStoreMetadata> mMetadata;
private:
// Only created by VersionChangeTransaction.
RenameObjectStoreOp(VersionChangeTransaction* aTransaction,
FullObjectStoreMetadata* const aMetadata)
: VersionChangeTransactionOp(aTransaction)
, mMetadata(aMetadata)
{
MOZ_ASSERT(aMetadata->mCommonMetadata.id());
}
~RenameObjectStoreOp()
{ }
virtual nsresult
DoDatabaseWork(DatabaseConnection* aConnection) override;
};
class CreateIndexOp final
: public VersionChangeTransactionOp
{
@ -7923,6 +7956,33 @@ private:
DoDatabaseWork(DatabaseConnection* aConnection) override;
};
class RenameIndexOp final
: public VersionChangeTransactionOp
{
friend class VersionChangeTransaction;
const RefPtr<FullIndexMetadata> mMetadata;
const int64_t mObjectStoreId;
private:
// Only created by VersionChangeTransaction.
RenameIndexOp(VersionChangeTransaction* aTransaction,
FullIndexMetadata* const aMetadata,
int64_t aObjectStoreId)
: VersionChangeTransactionOp(aTransaction)
, mMetadata(aMetadata)
, mObjectStoreId(aObjectStoreId)
{
MOZ_ASSERT(aMetadata->mCommonMetadata.id());
}
~RenameIndexOp()
{ }
virtual nsresult
DoDatabaseWork(DatabaseConnection* aConnection) override;
};
class NormalTransactionOp
: public TransactionDatabaseOperationBase
, public PBackgroundIDBRequestParent
@ -15562,6 +15622,54 @@ VersionChangeTransaction::RecvDeleteObjectStore(const int64_t& aObjectStoreId)
return true;
}
bool
VersionChangeTransaction::RecvRenameObjectStore(const int64_t& aObjectStoreId,
const nsString& aName)
{
AssertIsOnBackgroundThread();
if (NS_WARN_IF(!aObjectStoreId)) {
ASSERT_UNLESS_FUZZING();
return false;
}
const RefPtr<FullDatabaseMetadata> dbMetadata = GetDatabase()->Metadata();
MOZ_ASSERT(dbMetadata);
MOZ_ASSERT(dbMetadata->mNextObjectStoreId > 0);
if (NS_WARN_IF(aObjectStoreId >= dbMetadata->mNextObjectStoreId)) {
ASSERT_UNLESS_FUZZING();
return false;
}
RefPtr<FullObjectStoreMetadata> foundMetadata =
GetMetadataForObjectStoreId(aObjectStoreId);
if (NS_WARN_IF(!foundMetadata)) {
ASSERT_UNLESS_FUZZING();
return false;
}
if (NS_WARN_IF(mCommitOrAbortReceived)) {
ASSERT_UNLESS_FUZZING();
return false;
}
foundMetadata->mCommonMetadata.name() = aName;
RefPtr<RenameObjectStoreOp> renameOp =
new RenameObjectStoreOp(this, foundMetadata);
if (NS_WARN_IF(!renameOp->Init(this))) {
renameOp->Cleanup();
return false;
}
renameOp->DispatchToConnectionPool();
return true;
}
bool
VersionChangeTransaction::RecvCreateIndex(const int64_t& aObjectStoreId,
const IndexMetadata& aMetadata)
@ -15717,6 +15825,74 @@ VersionChangeTransaction::RecvDeleteIndex(const int64_t& aObjectStoreId,
return true;
}
bool
VersionChangeTransaction::RecvRenameIndex(const int64_t& aObjectStoreId,
const int64_t& aIndexId,
const nsString& aName)
{
AssertIsOnBackgroundThread();
if (NS_WARN_IF(!aObjectStoreId)) {
ASSERT_UNLESS_FUZZING();
return false;
}
if (NS_WARN_IF(!aIndexId)) {
ASSERT_UNLESS_FUZZING();
return false;
}
const RefPtr<FullDatabaseMetadata> dbMetadata = GetDatabase()->Metadata();
MOZ_ASSERT(dbMetadata);
MOZ_ASSERT(dbMetadata->mNextObjectStoreId > 0);
MOZ_ASSERT(dbMetadata->mNextIndexId > 0);
if (NS_WARN_IF(aObjectStoreId >= dbMetadata->mNextObjectStoreId)) {
ASSERT_UNLESS_FUZZING();
return false;
}
if (NS_WARN_IF(aIndexId >= dbMetadata->mNextIndexId)) {
ASSERT_UNLESS_FUZZING();
return false;
}
RefPtr<FullObjectStoreMetadata> foundObjectStoreMetadata =
GetMetadataForObjectStoreId(aObjectStoreId);
if (NS_WARN_IF(!foundObjectStoreMetadata)) {
ASSERT_UNLESS_FUZZING();
return false;
}
RefPtr<FullIndexMetadata> foundIndexMetadata =
GetMetadataForIndexId(foundObjectStoreMetadata, aIndexId);
if (NS_WARN_IF(!foundIndexMetadata)) {
ASSERT_UNLESS_FUZZING();
return false;
}
if (NS_WARN_IF(mCommitOrAbortReceived)) {
ASSERT_UNLESS_FUZZING();
return false;
}
foundIndexMetadata->mCommonMetadata.name() = aName;
RefPtr<RenameIndexOp> renameOp =
new RenameIndexOp(this, foundIndexMetadata, aObjectStoreId);
if (NS_WARN_IF(!renameOp->Init(this))) {
renameOp->Cleanup();
return false;
}
renameOp->DispatchToConnectionPool();
return true;
}
PBackgroundIDBRequestParent*
VersionChangeTransaction::AllocPBackgroundIDBRequestParent(
const RequestParams& aParams)
@ -23484,6 +23660,91 @@ DeleteObjectStoreOp::DoDatabaseWork(DatabaseConnection* aConnection)
return NS_OK;
}
nsresult
RenameObjectStoreOp::DoDatabaseWork(DatabaseConnection* aConnection)
{
MOZ_ASSERT(aConnection);
aConnection->AssertIsOnConnectionThread();
PROFILER_LABEL("IndexedDB",
"RenameObjectStoreOp::DoDatabaseWork",
js::ProfileEntry::Category::STORAGE);
if (NS_WARN_IF(IndexedDatabaseManager::InLowDiskSpaceMode())) {
return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
}
#ifdef DEBUG
{
// Make sure that we're not renaming an object store with the same name as
// another that already exists. This should be impossible because we should
// have thrown an error long before now...
DatabaseConnection::CachedStatement stmt;
MOZ_ALWAYS_SUCCEEDS(
aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
"SELECT name "
"FROM object_store "
"WHERE name = :name "
"AND id != :id;"),
&stmt));
MOZ_ALWAYS_SUCCEEDS(
stmt->BindStringByName(NS_LITERAL_CSTRING("name"),
mMetadata->mCommonMetadata.name()));
MOZ_ALWAYS_SUCCEEDS(
stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"),
mMetadata->mCommonMetadata.id()));
bool hasResult;
MOZ_ALWAYS_SUCCEEDS(stmt->ExecuteStep(&hasResult));
MOZ_ASSERT(!hasResult);
}
#endif
DatabaseConnection::AutoSavepoint autoSave;
nsresult rv = autoSave.Start(Transaction());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
DatabaseConnection::CachedStatement stmt;
rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
"UPDATE object_store "
"SET name = :name "
"WHERE id = :id;"),
&stmt);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"),
mMetadata->mCommonMetadata.name());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"),
mMetadata->mCommonMetadata.id());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = stmt->Execute();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = autoSave.Commit();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
CreateIndexOp::CreateIndexOp(VersionChangeTransaction* aTransaction,
const int64_t aObjectStoreId,
const IndexMetadata& aMetadata)
@ -23792,7 +24053,7 @@ NormalJSRuntime::Init()
}
// Not setting this will cause JS_CHECK_RECURSION to report false positives.
JS_SetNativeStackQuota(mRuntime, 128 * sizeof(size_t) * 1024);
JS_SetNativeStackQuota(mRuntime, 128 * sizeof(size_t) * 1024);
mContext = JS_NewContext(mRuntime, 0);
if (NS_WARN_IF(!mContext)) {
@ -24501,6 +24762,99 @@ DeleteIndexOp::DoDatabaseWork(DatabaseConnection* aConnection)
return NS_OK;
}
nsresult
RenameIndexOp::DoDatabaseWork(DatabaseConnection* aConnection)
{
MOZ_ASSERT(aConnection);
aConnection->AssertIsOnConnectionThread();
PROFILER_LABEL("IndexedDB",
"RenameIndexOp::DoDatabaseWork",
js::ProfileEntry::Category::STORAGE);
if (NS_WARN_IF(IndexedDatabaseManager::InLowDiskSpaceMode())) {
return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
}
#ifdef DEBUG
{
// Make sure that we're not renaming an index with the same name as another
// that already exists. This should be impossible because we should have
// thrown an error long before now...
DatabaseConnection::CachedStatement stmt;
MOZ_ALWAYS_SUCCEEDS(
aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
"SELECT name "
"FROM object_store_index "
"WHERE object_store_id = :object_store_id "
"AND name = :name "
"AND id != :id;"),
&stmt));
MOZ_ALWAYS_SUCCEEDS(
stmt->BindInt64ByName(NS_LITERAL_CSTRING("object_store_id"),
mObjectStoreId));
MOZ_ALWAYS_SUCCEEDS(
stmt->BindStringByName(NS_LITERAL_CSTRING("name"),
mMetadata->mCommonMetadata.name()));
MOZ_ALWAYS_SUCCEEDS(
stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"),
mMetadata->mCommonMetadata.id()));
bool hasResult;
MOZ_ALWAYS_SUCCEEDS(stmt->ExecuteStep(&hasResult));
MOZ_ASSERT(!hasResult);
}
#else
Unused << mObjectStoreId;
#endif
DatabaseConnection::AutoSavepoint autoSave;
nsresult rv = autoSave.Start(Transaction());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
DatabaseConnection::CachedStatement stmt;
rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
"UPDATE object_store_index "
"SET name = :name "
"WHERE id = :id;"),
&stmt);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"),
mMetadata->mCommonMetadata.name());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"),
mMetadata->mCommonMetadata.id());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = stmt->Execute();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = autoSave.Commit();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
// static
nsresult
NormalTransactionOp::ObjectStoreHasIndexes(NormalTransactionOp* aOp,

View File

@ -1349,5 +1349,85 @@ Observer::Observe(nsISupports* aSubject,
return NS_OK;
}
nsresult
IDBDatabase::RenameObjectStore(int64_t aObjectStoreId, const nsAString& aName)
{
MOZ_ASSERT(mSpec);
nsTArray<ObjectStoreSpec>& objectStores = mSpec->objectStores();
ObjectStoreSpec* foundObjectStoreSpec = nullptr;
// Find the matched object store spec and check if 'aName' is already used by
// another object store.
for (uint32_t objCount = objectStores.Length(), objIndex = 0;
objIndex < objCount;
objIndex++) {
const ObjectStoreSpec& objSpec = objectStores[objIndex];
if (objSpec.metadata().id() == aObjectStoreId) {
MOZ_ASSERT(!foundObjectStoreSpec);
foundObjectStoreSpec = &objectStores[objIndex];
continue;
}
if (aName == objSpec.metadata().name()) {
return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
}
}
MOZ_ASSERT(foundObjectStoreSpec);
// Update the name of the matched object store.
foundObjectStoreSpec->metadata().name() = nsString(aName);
return NS_OK;
}
nsresult
IDBDatabase::RenameIndex(int64_t aObjectStoreId,
int64_t aIndexId,
const nsAString& aName)
{
MOZ_ASSERT(mSpec);
nsTArray<ObjectStoreSpec>& objectStores = mSpec->objectStores();
ObjectStoreSpec* foundObjectStoreSpec = nullptr;
// Find the matched index metadata and check if 'aName' is already used by
// another index.
for (uint32_t objCount = objectStores.Length(), objIndex = 0;
objIndex < objCount;
objIndex++) {
const ObjectStoreSpec& objSpec = objectStores[objIndex];
if (objSpec.metadata().id() == aObjectStoreId) {
foundObjectStoreSpec = &objectStores[objIndex];
break;
}
}
MOZ_ASSERT(foundObjectStoreSpec);
nsTArray<IndexMetadata>& indexes = foundObjectStoreSpec->indexes();
IndexMetadata* foundIndexMetadata = nullptr;
for (uint32_t idxCount = indexes.Length(), idxIndex = 0;
idxIndex < idxCount;
idxIndex++) {
const IndexMetadata& metadata = indexes[idxIndex];
if (metadata.id() == aIndexId) {
MOZ_ASSERT(!foundIndexMetadata);
foundIndexMetadata = &indexes[idxIndex];
continue;
}
if (aName == metadata.name()) {
return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
}
}
MOZ_ASSERT(foundIndexMetadata);
// Update the name of the matched object store.
foundIndexMetadata->name() = nsString(aName);
return NS_OK;
}
} // namespace dom
} // namespace mozilla

View File

@ -56,6 +56,9 @@ class IDBDatabase final
class Observer;
friend class Observer;
friend class IDBObjectStore;
friend class IDBIndex;
// The factory must be kept alive when IndexedDB is used in multiple
// processes. If it dies then the entire actor tree will be destroyed with it
// and the world will explode.
@ -327,6 +330,14 @@ private:
const nsAString& aFilename,
uint32_t aLineNumber,
uint32_t aColumnNumber);
// Only accessed by IDBObjectStore.
nsresult
RenameObjectStore(int64_t aObjectStoreId, const nsAString& aName);
// Only accessed by IDBIndex.
nsresult
RenameIndex(int64_t aObjectStoreId, int64_t aIndexId, const nsAString& aName);
};
} // namespace dom

View File

@ -149,6 +149,63 @@ IDBIndex::Name() const
return mMetadata->name();
}
void
IDBIndex::SetName(const nsAString& aName, ErrorResult& aRv)
{
AssertIsOnOwningThread();
IDBTransaction* transaction = mObjectStore->Transaction();
if (transaction->GetMode() != IDBTransaction::VERSION_CHANGE ||
mDeletedMetadata) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
if (!transaction->IsOpen()) {
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
return;
}
if (aName == mMetadata->name()) {
return;
}
// Cache logging string of this index before renaming.
const LoggingString loggingOldIndex(this);
const int64_t indexId = Id();
nsresult rv =
transaction->Database()->RenameIndex(mObjectStore->Id(),
indexId,
aName);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return;
}
// Don't do this in the macro because we always need to increment the serial
// number to keep in sync with the parent.
const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: "
"database(%s).transaction(%s).objectStore(%s).index(%s)."
"rename(%s)",
"IndexedDB %s: C T[%lld] R[%llu]: IDBIndex.rename()",
IDB_LOG_ID_STRING(),
transaction->LoggingSerialNumber(),
requestSerialNumber,
IDB_LOG_STRINGIFY(transaction->Database()),
IDB_LOG_STRINGIFY(transaction),
IDB_LOG_STRINGIFY(mObjectStore),
loggingOldIndex.get(),
IDB_LOG_STRINGIFY(this));
transaction->RenameIndex(mObjectStore, indexId, aName);
}
bool
IDBIndex::Unique() const
{

View File

@ -106,6 +106,9 @@ public:
aName = Name();
}
void
SetName(const nsAString& aName, ErrorResult& aRv);
void
GetKeyPath(JSContext* aCx,
JS::MutableHandle<JS::Value> aResult,

View File

@ -2265,6 +2265,59 @@ IDBObjectStore::Name() const
return mSpec->metadata().name();
}
void
IDBObjectStore::SetName(const nsAString& aName, ErrorResult& aRv)
{
AssertIsOnOwningThread();
if (mTransaction->GetMode() != IDBTransaction::VERSION_CHANGE ||
mDeletedSpec) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
IDBTransaction* transaction = IDBTransaction::GetCurrent();
if (!transaction || transaction != mTransaction) {
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
return;
}
MOZ_ASSERT(transaction->IsOpen());
if (aName == mSpec->metadata().name()) {
return;
}
// Cache logging string of this object store before renaming.
const LoggingString loggingOldObjectStore(this);
nsresult rv =
transaction->Database()->RenameObjectStore(mSpec->metadata().id(),
aName);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return;
}
// Don't do this in the macro because we always need to increment the serial
// number to keep in sync with the parent.
const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: "
"database(%s).transaction(%s).objectStore(%s).rename(%s)",
"IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.rename()",
IDB_LOG_ID_STRING(),
mTransaction->LoggingSerialNumber(),
requestSerialNumber,
IDB_LOG_STRINGIFY(mTransaction->Database()),
IDB_LOG_STRINGIFY(mTransaction),
loggingOldObjectStore.get(),
IDB_LOG_STRINGIFY(this));
transaction->RenameObjectStore(mSpec->metadata().id(), aName);
}
bool
IDBObjectStore::AutoIncrement() const
{

View File

@ -151,6 +151,9 @@ public:
aName = Name();
}
void
SetName(const nsAString& aName, ErrorResult& aRv);
void
GetKeyPath(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
ErrorResult& aRv);

View File

@ -566,6 +566,20 @@ IDBTransaction::DeleteObjectStore(int64_t aObjectStoreId)
}
}
void
IDBTransaction::RenameObjectStore(int64_t aObjectStoreId,
const nsAString& aName)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aObjectStoreId);
MOZ_ASSERT(VERSION_CHANGE == mMode);
MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
MOZ_ASSERT(IsOpen());
MOZ_ALWAYS_TRUE(mBackgroundActor.mVersionChangeBackgroundActor->
SendRenameObjectStore(aObjectStoreId, nsString(aName)));
}
void
IDBTransaction::CreateIndex(IDBObjectStore* aObjectStore,
const indexedDB::IndexMetadata& aMetadata)
@ -596,6 +610,24 @@ IDBTransaction::DeleteIndex(IDBObjectStore* aObjectStore,
SendDeleteIndex(aObjectStore->Id(), aIndexId));
}
void
IDBTransaction::RenameIndex(IDBObjectStore* aObjectStore,
int64_t aIndexId,
const nsAString& aName)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aObjectStore);
MOZ_ASSERT(aIndexId);
MOZ_ASSERT(VERSION_CHANGE == mMode);
MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
MOZ_ASSERT(IsOpen());
MOZ_ALWAYS_TRUE(mBackgroundActor.mVersionChangeBackgroundActor->
SendRenameIndex(aObjectStore->Id(),
aIndexId,
nsString(aName)));
}
void
IDBTransaction::AbortInternal(nsresult aAbortCode,
already_AddRefed<DOMError> aError)

View File

@ -245,12 +245,18 @@ public:
void
DeleteObjectStore(int64_t aObjectStoreId);
void
RenameObjectStore(int64_t aObjectStoreId, const nsAString& aName);
void
CreateIndex(IDBObjectStore* aObjectStore, const indexedDB::IndexMetadata& aMetadata);
void
DeleteIndex(IDBObjectStore* aObjectStore, int64_t aIndexId);
void
RenameIndex(IDBObjectStore* aObjectStore, int64_t aIndexId, const nsAString& aName);
void
Abort(IDBRequest* aRequest);

View File

@ -30,11 +30,16 @@ parent:
async CreateObjectStore(ObjectStoreMetadata metadata);
async DeleteObjectStore(int64_t objectStoreId);
async RenameObjectStore(int64_t objectStoreId,
nsString name);
async CreateIndex(int64_t objectStoreId,
IndexMetadata metadata);
async DeleteIndex(int64_t objectStoreId,
int64_t indexId);
async RenameIndex(int64_t objectStoreId,
int64_t indexId,
nsString name);
async PBackgroundIDBCursor(OpenCursorParams params);

View File

@ -82,7 +82,11 @@ support-files =
unit/test_readonly_transactions.js
unit/test_readwriteflush_disabled.js
unit/test_remove_index.js
unit/test_rename_index.js
unit/test_rename_index_errors.js
unit/test_remove_objectStore.js
unit/test_rename_objectStore.js
unit/test_rename_objectStore_errors.js
unit/test_request_readyState.js
unit/test_setVersion.js
unit/test_setVersion_abort.js
@ -317,8 +321,16 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_remove_index.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_rename_index.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_rename_index_errors.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_remove_objectStore.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || (buildapp == 'mulet') # Bug 931116 # TC: Bug 1144079 - Re-enable Mulet mochitests and reftests taskcluster-specific disables.
[test_rename_objectStore.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_rename_objectStore_errors.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_request_readyState.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_sandbox.html]

View File

@ -0,0 +1,19 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>Indexed Database Property Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7" src="unit/test_rename_index.js"></script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

View File

@ -0,0 +1,19 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>Indexed Database Property Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7" src="unit/test_rename_index_errors.js"></script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

View File

@ -0,0 +1,19 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>Indexed Database Property Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7" src="unit/test_rename_objectStore.js"></script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

View File

@ -0,0 +1,19 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>Indexed Database Property Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7" src="unit/test_rename_objectStore_errors.js"></script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

View File

@ -0,0 +1,193 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
var testGenerator = testSteps();
function testSteps()
{
const name = this.window ? window.location.pathname : "Splendid Test";
const storeName = "test store";
const indexName_ToBeDeleted = "test index to be deleted";
const indexName_v0 = "test index v0";
const indexName_v1 = "test index v1";
const indexName_v2 = "test index v2";
const indexName_v3 = indexName_ToBeDeleted;
const indexName_v4 = "test index v4";
info("Rename in v1.");
let request = indexedDB.open(name, 1);
request.onerror = errorHandler;
request.onupgradeneeded = grabEventAndContinueHandler;
request.onsuccess = unexpectedSuccessHandler;
let event = yield undefined;
let db = event.target.result;
let txn = event.target.transaction;
is(db.objectStoreNames.length, 0, "Correct objectStoreNames list");
let objectStore = db.createObjectStore(storeName, { keyPath: "foo" });
is(db.objectStoreNames.length, 1, "Correct objectStoreNames list");
is(db.objectStoreNames.item(0), objectStore.name, "Correct object store name");
// create index to be deleted later in v3.
objectStore.createIndex(indexName_ToBeDeleted, "foo");
ok(objectStore.index(indexName_ToBeDeleted), "Index created.");
// create target index to be renamed.
let index = objectStore.createIndex(indexName_v0, "bar");
ok(objectStore.index(indexName_v0), "Index created.");
is(index.name, indexName_v0, "Correct index name");
index.name = indexName_v1;
is(index.name, indexName_v1, "Renamed index successfully");
txn.oncomplete = continueToNextStepSync;
yield undefined;
request.onsuccess = continueToNextStep;
yield undefined;
db.close();
info("Verify renaming done in v1 and run renaming in v2.");
request = indexedDB.open(name, 2);
request.onerror = errorHandler;
request.onupgradeneeded = grabEventAndContinueHandler;
request.onsuccess = unexpectedSuccessHandler;
event = yield undefined;
db = event.target.result;
txn = event.target.transaction;
objectStore = txn.objectStore(storeName);
// indexName_v0 created in v1 shall not be available.
try {
index = objectStore.index(indexName_v0);
ok(false, "NotFoundError shall be thrown.");
} catch (e) {
ok(e instanceof DOMException, "got a database exception");
is(e.name, "NotFoundError", "correct error");
}
// rename to "v2".
index = objectStore.index(indexName_v1);
is(index.name, indexName_v1, "Correct index name")
index.name = indexName_v2;
is(index.name, indexName_v2, "Renamed index successfully");
txn.oncomplete = continueToNextStepSync;
yield undefined;
request.onsuccess = continueToNextStep;
yield undefined;
db.close();
info("Verify renaming done in v2.");
request = indexedDB.open(name, 2);
request.onerror = errorHandler;
request.onupgradeneeded = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
db = event.target.result;
txn = db.transaction(storeName);
objectStore = txn.objectStore(storeName);
index = objectStore.index(indexName_v2);
is(index.name, indexName_v2, "Correct index name");
db.close();
info("Rename in v3.");
request = indexedDB.open(name, 3);
request.onerror = errorHandler;
request.onupgradeneeded = grabEventAndContinueHandler;
request.onsuccess = unexpectedSuccessHandler;
event = yield undefined;
db = event.target.result;
txn = event.target.transaction;
objectStore = txn.objectStore(storeName);
ok(objectStore.index(indexName_ToBeDeleted), "index is valid.");
objectStore.deleteIndex(indexName_ToBeDeleted);
try {
objectStore.index(indexName_ToBeDeleted);
ok(false, "NotFoundError shall be thrown if the index name is deleted.");
} catch (e) {
ok(e instanceof DOMException, "got a database exception");
is(e.name, "NotFoundError", "correct error");
}
info("Rename with the name of the deleted index.");
index = objectStore.index(indexName_v2);
index.name = indexName_v3;
is(index.name, indexName_v3, "Renamed index successfully");
txn.oncomplete = continueToNextStepSync;
yield undefined;
request.onsuccess = continueToNextStep;
yield undefined;
db.close();
info("Verify renaming done in v3.");
request = indexedDB.open(name, 3);
request.onerror = errorHandler;
request.onupgradeneeded = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
db = event.target.result;
txn = db.transaction(storeName);
objectStore = txn.objectStore(storeName);
index = objectStore.index(indexName_v3);
is(index.name, indexName_v3, "Correct index name");
db.close();
info("Abort the version change transaction while renaming index.");
request = indexedDB.open(name, 4);
request.onerror = errorHandler;
request.onupgradeneeded = grabEventAndContinueHandler;
request.onsuccess = unexpectedSuccessHandler;
event = yield undefined;
db = event.target.result;
txn = event.target.transaction;
objectStore = txn.objectStore(storeName);
index = objectStore.index(indexName_v3);
index.name = indexName_v4;
is(index.name, indexName_v4, "Renamed successfully");
let putRequest = objectStore.put({ foo: "fooValue", bar: "barValue" });
putRequest.onsuccess = continueToNextStepSync;
yield undefined;
// Aborting the transaction.
request.onerror = expectedErrorHandler("AbortError");
txn.abort();
yield undefined;
// Verify if the name of the index handle is reverted.
is(index.name, indexName_v3, "The name is reverted after aborted.");
info("Verify if the objectstore name is unchanged.");
request = indexedDB.open(name, 3);
request.onerror = errorHandler;
request.onupgradeneeded = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
db = event.target.result;
txn = db.transaction(storeName);
objectStore = txn.objectStore(storeName);
index = objectStore.index(indexName_v3);
is(index.name, indexName_v3, "Correct index name");
db.close();
finishTest();
yield undefined;
}

View File

@ -0,0 +1,129 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
var testGenerator = testSteps();
function testSteps()
{
const name = this.window ? window.location.pathname : "Splendid Test";
const storeName = "test store";
const indexName1 = "test index 1";
const indexName2 = "test index 2";
info("Setup test indexes.");
let request = indexedDB.open(name, 1);
request.onerror = errorHandler;
request.onupgradeneeded = grabEventAndContinueHandler;
request.onsuccess = unexpectedSuccessHandler;
let event = yield undefined;
let db = event.target.result;
let txn = event.target.transaction;
is(db.objectStoreNames.length, 0, "Correct objectStoreNames list");
let objectStore = db.createObjectStore(storeName, { keyPath: "foo" });
is(db.objectStoreNames.length, 1, "Correct objectStoreNames list");
is(db.objectStoreNames.item(0), objectStore.name, "Correct name");
let index1 = objectStore.createIndex(indexName1, "bar");
is(objectStore.index(indexName1).name, index1.name, "Correct index name");
is(index1.name, indexName1, "Correct index name");
let index2 = objectStore.createIndex(indexName2, "baz");
is(objectStore.index(indexName2).name, index2.name, "Correct index name");
is(index2.name, indexName2, "Correct index name");
txn.oncomplete = continueToNextStepSync;
yield undefined;
request.onsuccess = continueToNextStep;
yield undefined;
db.close();
info("Verify IDB Errors in version 2.");
request = indexedDB.open(name, 2);
request.onerror = errorHandler;
request.onupgradeneeded = grabEventAndContinueHandler;
request.onsuccess = unexpectedSuccessHandler;
event = yield undefined;
db = event.target.result;
txn = event.target.transaction;
objectStore = txn.objectStore(storeName);
index1 = objectStore.index(indexName1);
index2 = objectStore.index(indexName2);
is(index1.name, indexName1, "Correct index name");
is(index2.name, indexName2, "Correct index name");
// Rename with the name already adopted by the other index.
try {
index1.name = indexName2;
ok(false, "ConstraintError shall be thrown if the index name already exists.");
} catch (e) {
ok(e instanceof DOMException, "got a database exception");
is(e.name, "ConstraintError", "correct error");
}
// Rename with identical name.
try {
index1.name = indexName1;
ok(true, "It shall be fine to set the same name.");
} catch (e) {
ok(false, "Got a database exception: " + e.name);
}
objectStore.deleteIndex(indexName2);
// Rename after deleted.
try {
index2.name = indexName2;
ok(false, "InvalidStateError shall be thrown if deleted.");
} catch (e) {
ok(e instanceof DOMException, "got a database exception");
is(e.name, "InvalidStateError", "correct error");
}
txn.oncomplete = continueToNextStepSync;
yield undefined;
request.onsuccess = continueToNextStep;
yield undefined;
db.close();
// Rename when the transaction is inactive.
try {
index1.name = indexName1;
ok(false, "TransactionInactiveError shall be thrown if the transaction is inactive.");
} catch (e) {
ok(e instanceof DOMException, "got a database exception");
is(e.name, "TransactionInactiveError", "correct error");
}
info("Rename when the transaction is not an upgrade one.");
request = indexedDB.open(name, 2);
request.onerror = errorHandler;
request.onupgradeneeded = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
db = event.target.result;
txn = db.transaction(storeName);
objectStore = txn.objectStore(storeName);
index1 = objectStore.index(indexName1);
try {
index1.name = indexName1;
ok(false, "InvalidStateError shall be thrown if it's not an upgrade transaction.");
} catch (e) {
ok(e instanceof DOMException, "got a database exception");
is(e.name, "InvalidStateError", "correct error");
}
txn.oncomplete = continueToNextStepSync;
yield undefined;
db.close();
finishTest();
yield undefined;
}

View File

@ -0,0 +1,171 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
var testGenerator = testSteps();
function testSteps()
{
const name = this.window ? window.location.pathname : "Splendid Test";
const storeName_ToBeDeleted = "test store to be deleted";
const storeName_v0 = "test store v0";
const storeName_v1 = "test store v1";
const storeName_v2 = "test store v2";
const storeName_v3 = storeName_ToBeDeleted;
const storeName_v4 = "test store v4";
info("Rename in v1.");
let request = indexedDB.open(name, 1);
request.onerror = errorHandler;
request.onupgradeneeded = grabEventAndContinueHandler;
request.onsuccess = unexpectedSuccessHandler;
let event = yield undefined;
let db = event.target.result;
let txn = event.target.transaction;
is(db.objectStoreNames.length, 0, "Correct objectStoreNames list");
// create objectstore to be deleted later in v3.
db.createObjectStore(storeName_ToBeDeleted, { keyPath: "foo" });
is(db.objectStoreNames.length, 1, "Correct objectStoreNames list");
ok(db.objectStoreNames.contains(storeName_ToBeDeleted), "Correct name");
// create target objectstore to be renamed.
let objectStore = db.createObjectStore(storeName_v0, { keyPath: "bar" });
is(db.objectStoreNames.length, 2, "Correct objectStoreNames list");
ok(db.objectStoreNames.contains(objectStore.name), "Correct name");
objectStore.name = storeName_v1;
is(objectStore.name, storeName_v1, "Renamed successfully");
txn.oncomplete = continueToNextStepSync;
yield undefined;
request.onsuccess = continueToNextStep;
yield undefined;
db.close();
info("Verify renaming done in v1 and run renaming in v2.");
request = indexedDB.open(name, 2);
request.onerror = errorHandler;
request.onupgradeneeded = grabEventAndContinueHandler;
request.onsuccess = unexpectedSuccessHandler;
event = yield undefined;
db = event.target.result;
txn = event.target.transaction;
is(db.objectStoreNames.length, 2, "Correct objectStoreNames list");
ok(db.objectStoreNames.contains(storeName_v1), "Correct name");
ok(db.objectStoreNames.contains(storeName_ToBeDeleted), "Correct name");
objectStore = txn.objectStore(storeName_v1);
objectStore.name = storeName_v2;
is(objectStore.name, storeName_v2, "Renamed successfully");
txn.oncomplete = continueToNextStepSync;
yield undefined;
request.onsuccess = continueToNextStep;
yield undefined;
db.close();
info("Verify renaming done in v2.");
request = indexedDB.open(name, 2);
request.onerror = errorHandler;
request.onupgradeneeded = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
db = event.target.result;
is(db.objectStoreNames.length, 2, "Correct objectStoreNames list");
ok(db.objectStoreNames.contains(storeName_v2), "Correct name");
ok(db.objectStoreNames.contains(storeName_ToBeDeleted), "Correct name");
db.close();
info("Rename in v3.");
request = indexedDB.open(name, 3);
request.onerror = errorHandler;
request.onupgradeneeded = grabEventAndContinueHandler;
request.onsuccess = unexpectedSuccessHandler;
event = yield undefined;
db = event.target.result;
txn = event.target.transaction;
is(db.objectStoreNames.length, 2, "Correct objectStoreNames list");
ok(db.objectStoreNames.contains(storeName_v2), "Correct name");
ok(db.objectStoreNames.contains(storeName_ToBeDeleted), "Correct name");
db.deleteObjectStore(storeName_ToBeDeleted);
is(db.objectStoreNames.length, 1, "Correct objectStoreNames list");
ok(db.objectStoreNames.contains(storeName_v2) &&
!db.objectStoreNames.contains(storeName_ToBeDeleted), "Deleted correctly");
objectStore = txn.objectStore(storeName_v2);
objectStore.name = storeName_v3;
is(objectStore.name, storeName_v3, "Renamed successfully");
txn.oncomplete = continueToNextStepSync;
yield undefined;
request.onsuccess = continueToNextStep;
yield undefined;
db.close();
info("Verify renaming done in v3.");
request = indexedDB.open(name, 3);
request.onerror = errorHandler;
request.onupgradeneeded = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
db = event.target.result;
is(db.objectStoreNames.length, 1, "Correct objectStoreNames list");
ok(db.objectStoreNames.contains(storeName_v3), "Correct name");
db.close();
info("Abort the version change transaction while renaming objectstore.");
request = indexedDB.open(name, 4);
request.onerror = errorHandler;
request.onupgradeneeded = grabEventAndContinueHandler;
request.onsuccess = unexpectedSuccessHandler;
event = yield undefined;
db = event.target.result;
txn = event.target.transaction;
objectStore = txn.objectStore(storeName_v3);
objectStore.name = storeName_v4;
is(objectStore.name, storeName_v4, "Renamed successfully");
let putRequest = objectStore.put({ bar: "barValue" });
putRequest.onsuccess = continueToNextStepSync;
yield undefined;
// Aborting the transaction.
request.onerror = expectedErrorHandler("AbortError");
txn.abort();
yield undefined;
// Verify if the name of the objectStore handle is reverted.
is(objectStore.name, storeName_v3, "The name is reverted after aborted.");
info("Verify if the objectstore name is unchanged.");
request = indexedDB.open(name, 3);
request.onerror = errorHandler;
request.onupgradeneeded = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
db = event.target.result;
is(db.objectStoreNames.length, 1, "Correct objectStoreNames list");
ok(db.objectStoreNames.contains(storeName_v3), "Correct name");
db.close();
finishTest();
yield undefined;
}

View File

@ -0,0 +1,127 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
var testGenerator = testSteps();
function testSteps()
{
const name = this.window ? window.location.pathname : "Splendid Test";
const storeName1 = "test store 1";
const storeName2 = "test store 2";
info("Setup test object stores.");
let request = indexedDB.open(name, 1);
request.onerror = errorHandler;
request.onupgradeneeded = grabEventAndContinueHandler;
request.onsuccess = unexpectedSuccessHandler;
let event = yield undefined;
let db = event.target.result;
let txn = event.target.transaction;
is(db.objectStoreNames.length, 0, "Correct objectStoreNames list");
let objectStore1 = db.createObjectStore(storeName1, { keyPath: "foo" });
is(db.objectStoreNames.length, 1, "Correct objectStoreNames list");
is(db.objectStoreNames.item(0), objectStore1.name, "Correct name");
is(objectStore1.name, storeName1, "Correct name");
let objectStore2 = db.createObjectStore(storeName2, { keyPath: "bar" });
is(db.objectStoreNames.length, 2, "Correct objectStoreNames list");
is(db.objectStoreNames.item(1), objectStore2.name, "Correct name");
is(objectStore2.name, storeName2, "Correct name");
txn.oncomplete = continueToNextStepSync;
yield undefined;
request.onsuccess = continueToNextStep;
yield undefined;
db.close();
info("Verify IDB Errors in version 2.");
request = indexedDB.open(name, 2);
request.onerror = errorHandler;
request.onupgradeneeded = grabEventAndContinueHandler;
request.onsuccess = continueToNextStep;
event = yield undefined;
db = event.target.result;
txn = event.target.transaction;
is(db.objectStoreNames.length, 2, "Correct objectStoreNames list");
objectStore1 = txn.objectStore(storeName1);
objectStore2 = txn.objectStore(storeName2);
is(objectStore1.name, storeName1, "Correct name");
is(objectStore2.name, storeName2, "Correct name");
// Rename with the name already adopted by the other object store.
try {
objectStore1.name = storeName2;
ok(false, "ConstraintError shall be thrown if the store name already exists.");
} catch (e) {
ok(e instanceof DOMException, "got a database exception");
is(e.name, "ConstraintError", "correct error");
}
// Rename with the identical name.
try {
objectStore1.name = storeName1;
ok(true, "It shall be fine to set the same name.");
} catch (e) {
ok(false, "Got a database exception: " + e.name);
}
db.deleteObjectStore(storeName2);
// Rename after deleted.
try {
objectStore2.name = storeName2;
ok(false, "InvalidStateError shall be thrown if deleted.");
} catch (e) {
ok(e instanceof DOMException, "got a database exception");
is(e.name, "InvalidStateError", "correct error");
}
txn.oncomplete = continueToNextStepSync;
yield undefined;
request.onsuccess = continueToNextStep;
yield undefined;
db.close();
info("Rename when the transaction is inactive.");
try {
objectStore1.name = storeName1;
ok(false, "TransactionInactiveError shall be thrown if the transaction is inactive.");
} catch (e) {
ok(e instanceof DOMException, "got a database exception");
is(e.name, "TransactionInactiveError", "correct error");
}
info("Rename when the transaction is not an upgrade one.");
request = indexedDB.open(name, 2);
request.onerror = errorHandler;
request.onupgradeneeded = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
db = event.target.result;
txn = db.transaction(storeName1);
objectStore1 = txn.objectStore(storeName1);
try {
objectStore1.name = storeName1;
ok(false, "InvalidStateError shall be thrown if it's not an upgrade transaction.");
} catch (e) {
ok(e instanceof DOMException, "got a database exception");
is(e.name, "InvalidStateError", "correct error");
}
txn.oncomplete = continueToNextStepSync;
yield undefined;
db.close();
finishTest();
yield undefined;
}

View File

@ -65,7 +65,11 @@ skip-if = toolkit == 'android' # bug 864843
[test_put_get_values_autoIncrement.js]
[test_readonly_transactions.js]
[test_remove_index.js]
[test_rename_index.js]
[test_rename_index_errors.js]
[test_remove_objectStore.js]
[test_rename_objectStore.js]
[test_rename_objectStore_errors.js]
[test_request_readyState.js]
[test_sandbox.js]
[test_setVersion.js]

View File

@ -20,7 +20,9 @@ dictionary IDBIndexParameters {
[Exposed=(Window,Worker,System)]
interface IDBIndex {
readonly attribute DOMString name;
[SetterThrows]
attribute DOMString name;
readonly attribute IDBObjectStore objectStore;
[Throws]

View File

@ -14,7 +14,8 @@ dictionary IDBObjectStoreParameters {
[Exposed=(Window,Worker,System)]
interface IDBObjectStore {
readonly attribute DOMString name;
[SetterThrows]
attribute DOMString name;
[Throws]
readonly attribute any keyPath;

View File

@ -88,7 +88,7 @@ interface IDBDatabase : EventTarget {
};
interface IDBObjectStore {
readonly attribute DOMString name;
attribute DOMString name;
readonly attribute any keyPath;
readonly attribute DOMStringList indexNames;
readonly attribute IDBTransaction transaction;
@ -106,7 +106,7 @@ interface IDBObjectStore {
};
interface IDBIndex {
readonly attribute DOMString name;
attribute DOMString name;
readonly attribute IDBObjectStore objectStore;
readonly attribute any keyPath;
readonly attribute boolean multiEntry;