mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 765839 - 'Enable IndexedDB OOP test suite'. r=khuey.
--HG-- extra : transplant_source : %3Bb4%A4%99fC%9Cg%86%9B%1F3%C6%0F%01T%1C%3C%AE
This commit is contained in:
parent
9d93de75c0
commit
bfa7cae37b
@ -11,6 +11,7 @@
|
||||
|
||||
#include "nsIDOMFileHandle.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsIFileStorage.h"
|
||||
|
||||
#include "nsDOMEventTargetHelper.h"
|
||||
|
||||
|
@ -105,6 +105,16 @@ ObjectStoreInfo::~ObjectStoreInfo()
|
||||
}
|
||||
|
||||
IndexUpdateInfo::IndexUpdateInfo()
|
||||
: indexId(0),
|
||||
indexUnique(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(IndexUpdateInfo);
|
||||
}
|
||||
|
||||
IndexUpdateInfo::IndexUpdateInfo(const IndexUpdateInfo& aOther)
|
||||
: indexId(aOther.indexId),
|
||||
indexUnique(aOther.indexUnique),
|
||||
value(aOther.value)
|
||||
{
|
||||
MOZ_COUNT_CTOR(IndexUpdateInfo);
|
||||
}
|
||||
@ -113,6 +123,7 @@ IndexUpdateInfo::~IndexUpdateInfo()
|
||||
{
|
||||
MOZ_COUNT_DTOR(IndexUpdateInfo);
|
||||
}
|
||||
|
||||
#endif /* NS_BUILD_REFCNT_LOGGING */
|
||||
|
||||
// static
|
||||
|
@ -173,6 +173,7 @@ struct IndexUpdateInfo
|
||||
{
|
||||
#ifdef NS_BUILD_REFCNT_LOGGING
|
||||
IndexUpdateInfo();
|
||||
IndexUpdateInfo(const IndexUpdateInfo& aOther);
|
||||
~IndexUpdateInfo();
|
||||
#endif
|
||||
|
||||
|
@ -4,7 +4,6 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "IndexedDatabaseManager.h"
|
||||
#include "FileInfo.h"
|
||||
|
||||
USING_INDEXEDDB_NAMESPACE
|
||||
|
@ -8,8 +8,10 @@
|
||||
#define mozilla_dom_indexeddb_fileinfo_h__
|
||||
|
||||
#include "IndexedDatabase.h"
|
||||
|
||||
#include "nsAtomicRefcnt.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#include "FileManager.h"
|
||||
#include "IndexedDatabaseManager.h"
|
||||
|
||||
|
@ -243,6 +243,10 @@ IDBDatabase::Invalidate()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
if (IsInvalidated()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure we're closed too.
|
||||
Close();
|
||||
|
||||
|
@ -8,10 +8,11 @@
|
||||
|
||||
#include "nsIStandardFileStream.h"
|
||||
|
||||
#include "mozilla/dom/file/File.h"
|
||||
#include "nsDOMClassInfoID.h"
|
||||
|
||||
#include "FileStream.h"
|
||||
#include "mozilla/dom/file/File.h"
|
||||
#include "IDBDatabase.h"
|
||||
|
||||
USING_INDEXEDDB_NAMESPACE
|
||||
|
||||
|
@ -24,6 +24,7 @@ BEGIN_INDEXEDDB_NAMESPACE
|
||||
class AsyncConnectionHelper;
|
||||
class IDBCursor;
|
||||
class IDBKeyRange;
|
||||
class IDBRequest;
|
||||
class IndexedDBObjectStoreChild;
|
||||
class IndexedDBObjectStoreParent;
|
||||
class Key;
|
||||
|
@ -250,6 +250,7 @@ IDBTransaction::CommitOrRollback()
|
||||
NS_ASSERTION(mActorChild, "Must have an actor!");
|
||||
|
||||
mActorChild->SendAllRequestsFinished();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "IndexedDatabaseManager.h"
|
||||
#include "DatabaseInfo.h"
|
||||
|
||||
#include "nsIAtom.h"
|
||||
#include "nsIDOMScriptObjectFactory.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsIFileStorage.h"
|
||||
@ -437,33 +438,25 @@ IndexedDatabaseManager::AllowNextSynchronizedOp(const nsACString& aOrigin,
|
||||
}
|
||||
|
||||
nsresult
|
||||
IndexedDatabaseManager::AcquireExclusiveAccess(const nsACString& aOrigin,
|
||||
IDBDatabase* aDatabase,
|
||||
AsyncConnectionHelper* aHelper,
|
||||
WaitingOnDatabasesCallback aCallback,
|
||||
void* aClosure)
|
||||
IndexedDatabaseManager::AcquireExclusiveAccess(
|
||||
const nsACString& aOrigin,
|
||||
IDBDatabase* aDatabase,
|
||||
AsyncConnectionHelper* aHelper,
|
||||
nsIRunnable* aRunnable,
|
||||
WaitingOnDatabasesCallback aCallback,
|
||||
void* aClosure)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ASSERTION(aHelper, "Why are you talking to me?");
|
||||
NS_ASSERTION(!aDatabase || aHelper, "Need a helper with a database!");
|
||||
NS_ASSERTION(aDatabase || aRunnable, "Need a runnable without a database!");
|
||||
|
||||
// Find the right SynchronizedOp.
|
||||
SynchronizedOp* op = nsnull;
|
||||
PRUint32 count = mSynchronizedOps.Length();
|
||||
for (PRUint32 index = 0; index < count; index++) {
|
||||
SynchronizedOp* currentop = mSynchronizedOps[index].get();
|
||||
if (currentop->mOrigin.Equals(aOrigin)) {
|
||||
if (!currentop->mId ||
|
||||
(aDatabase && currentop->mId == aDatabase->Id())) {
|
||||
// We've found the right one.
|
||||
NS_ASSERTION(!currentop->mHelper,
|
||||
"SynchronizedOp already has a helper?!?");
|
||||
op = currentop;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
SynchronizedOp* op =
|
||||
FindSynchronizedOp(aOrigin, aDatabase ? aDatabase->Id() : nsnull);
|
||||
|
||||
NS_ASSERTION(op, "We didn't find a SynchronizedOp?");
|
||||
NS_ASSERTION(!op->mHelper, "SynchronizedOp already has a helper?!?");
|
||||
NS_ASSERTION(!op->mRunnable, "SynchronizedOp already has a runnable?!?");
|
||||
|
||||
nsTArray<IDBDatabase*>* array;
|
||||
mLiveDatabases.Get(aOrigin, &array);
|
||||
@ -474,33 +467,50 @@ IndexedDatabaseManager::AcquireExclusiveAccess(const nsACString& aOrigin,
|
||||
nsTArray<nsRefPtr<IDBDatabase> > liveDatabases;
|
||||
|
||||
if (array) {
|
||||
PRUint32 count = array->Length();
|
||||
for (PRUint32 index = 0; index < count; index++) {
|
||||
IDBDatabase*& database = array->ElementAt(index);
|
||||
if (!database->IsClosed() &&
|
||||
(!aDatabase ||
|
||||
(aDatabase &&
|
||||
if (aDatabase) {
|
||||
// Grab all databases that are not yet closed but whose database id match
|
||||
// the one we're looking for.
|
||||
for (PRUint32 index = 0; index < array->Length(); index++) {
|
||||
IDBDatabase*& database = array->ElementAt(index);
|
||||
if (!database->IsClosed() &&
|
||||
database != aDatabase &&
|
||||
database->Id() == aDatabase->Id()))) {
|
||||
liveDatabases.AppendElement(database);
|
||||
database->Id() == aDatabase->Id()) {
|
||||
liveDatabases.AppendElement(database);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// We want *all* databases, even those that are closed, if we're going to
|
||||
// clear the origin.
|
||||
liveDatabases.AppendElements(*array);
|
||||
}
|
||||
}
|
||||
|
||||
if (liveDatabases.IsEmpty()) {
|
||||
IndexedDatabaseManager::DispatchHelper(aHelper);
|
||||
return NS_OK;
|
||||
op->mHelper = aHelper;
|
||||
op->mRunnable = aRunnable;
|
||||
|
||||
if (!liveDatabases.IsEmpty()) {
|
||||
NS_ASSERTION(op->mDatabases.IsEmpty(),
|
||||
"How do we already have databases here?");
|
||||
op->mDatabases.AppendElements(liveDatabases);
|
||||
|
||||
// Give our callback the databases so it can decide what to do with them.
|
||||
aCallback(liveDatabases, aClosure);
|
||||
|
||||
NS_ASSERTION(liveDatabases.IsEmpty(),
|
||||
"Should have done something with the array!");
|
||||
|
||||
if (aDatabase) {
|
||||
// Wait for those databases to close.
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
NS_ASSERTION(op->mDatabases.IsEmpty(), "How do we already have databases here?");
|
||||
op->mDatabases.AppendElements(liveDatabases);
|
||||
op->mHelper = aHelper;
|
||||
// If we're trying to open a database and nothing blocks it, or if we're
|
||||
// clearing an origin, then go ahead and schedule the op.
|
||||
nsresult rv = RunSynchronizedOp(aDatabase, op);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Give our callback the databases so it can decide what to do with them.
|
||||
aCallback(liveDatabases, aClosure);
|
||||
|
||||
NS_ASSERTION(liveDatabases.IsEmpty(),
|
||||
"Should have done something with the array!");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -585,59 +595,23 @@ IndexedDatabaseManager::OnDatabaseClosed(IDBDatabase* aDatabase)
|
||||
|
||||
// Check through the list of SynchronizedOps to see if any are waiting for
|
||||
// this database to close before proceeding.
|
||||
PRUint32 count = mSynchronizedOps.Length();
|
||||
for (PRUint32 index = 0; index < count; index++) {
|
||||
nsAutoPtr<SynchronizedOp>& op = mSynchronizedOps[index];
|
||||
|
||||
if (op->mOrigin == aDatabase->Origin() &&
|
||||
(op->mId == aDatabase->Id() || !op->mId)) {
|
||||
// This database is in the scope of this SynchronizedOp. Remove it
|
||||
// from the list if necessary.
|
||||
if (op->mDatabases.RemoveElement(aDatabase)) {
|
||||
// Now set up the helper if there are no more live databases.
|
||||
NS_ASSERTION(op->mHelper, "How did we get rid of the helper before "
|
||||
"removing the last database?");
|
||||
if (op->mDatabases.IsEmpty()) {
|
||||
// At this point, all databases are closed, so no new transactions
|
||||
// can be started. There may, however, still be outstanding
|
||||
// transactions that have not completed. We need to wait for those
|
||||
// before we dispatch the helper.
|
||||
|
||||
FileService* service = FileService::Get();
|
||||
TransactionThreadPool* pool = TransactionThreadPool::Get();
|
||||
|
||||
PRUint32 count = !!service + !!pool;
|
||||
|
||||
nsRefPtr<WaitForTransactionsToFinishRunnable> runnable =
|
||||
new WaitForTransactionsToFinishRunnable(op,
|
||||
NS_MAX<PRUint32>(count, 1));
|
||||
|
||||
if (!count) {
|
||||
runnable->Run();
|
||||
}
|
||||
else {
|
||||
// Use the WaitForTransactionsToxFinishRunnable as the callback.
|
||||
|
||||
if (service) {
|
||||
nsTArray<nsCOMPtr<nsIFileStorage> > array;
|
||||
array.AppendElement(aDatabase);
|
||||
|
||||
if (!service->WaitForAllStoragesToComplete(array, runnable)) {
|
||||
NS_WARNING("Failed to wait for storages to complete!");
|
||||
}
|
||||
}
|
||||
|
||||
if (pool) {
|
||||
nsTArray<nsRefPtr<IDBDatabase> > array;
|
||||
array.AppendElement(aDatabase);
|
||||
|
||||
if (!pool->WaitForAllDatabasesToComplete(array, runnable)) {
|
||||
NS_WARNING("Failed to wait for databases to complete!");
|
||||
}
|
||||
}
|
||||
}
|
||||
SynchronizedOp* op = FindSynchronizedOp(aDatabase->Origin(), aDatabase->Id());
|
||||
if (op) {
|
||||
// This database is in the scope of this SynchronizedOp. Remove it
|
||||
// from the list if necessary.
|
||||
if (op->mDatabases.RemoveElement(aDatabase)) {
|
||||
// Now set up the helper if there are no more live databases.
|
||||
NS_ASSERTION(op->mHelper || op->mRunnable,
|
||||
"How did we get rid of the helper/runnable before "
|
||||
"removing the last database?");
|
||||
if (op->mDatabases.IsEmpty()) {
|
||||
// At this point, all databases are closed, so no new transactions
|
||||
// can be started. There may, however, still be outstanding
|
||||
// transactions that have not completed. We need to wait for those
|
||||
// before we dispatch the helper.
|
||||
if (NS_FAILED(RunSynchronizedOp(aDatabase, op))) {
|
||||
NS_WARNING("Failed to run synchronized op!");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1101,41 +1075,62 @@ IndexedDatabaseManager::AsyncDeleteFile(FileManager* aFileManager,
|
||||
|
||||
// static
|
||||
nsresult
|
||||
IndexedDatabaseManager::DispatchHelper(AsyncConnectionHelper* aHelper)
|
||||
IndexedDatabaseManager::RunSynchronizedOp(IDBDatabase* aDatabase,
|
||||
SynchronizedOp* aOp)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ASSERTION(aOp, "Null pointer!");
|
||||
NS_ASSERTION(!aDatabase || aOp->mHelper, "No helper on this op!");
|
||||
NS_ASSERTION(aDatabase || aOp->mRunnable, "No runnable on this op!");
|
||||
NS_ASSERTION(!aDatabase || aOp->mDatabases.IsEmpty(),
|
||||
"This op isn't ready to run!");
|
||||
|
||||
// If the helper has a transaction, dispatch it to the transaction
|
||||
// threadpool.
|
||||
if (aHelper->HasTransaction()) {
|
||||
rv = aHelper->DispatchToTransactionPool();
|
||||
FileService* service = FileService::Get();
|
||||
TransactionThreadPool* pool = TransactionThreadPool::Get();
|
||||
|
||||
nsTArray<nsRefPtr<IDBDatabase> > databases;
|
||||
if (aDatabase) {
|
||||
if (service || pool) {
|
||||
databases.AppendElement(aDatabase);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Otherwise, dispatch it to the IO thread.
|
||||
IndexedDatabaseManager* manager = IndexedDatabaseManager::Get();
|
||||
NS_ASSERTION(manager, "We should definitely have a manager here");
|
||||
|
||||
rv = aHelper->Dispatch(manager->IOThread());
|
||||
aOp->mDatabases.SwapElements(databases);
|
||||
}
|
||||
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return rv;
|
||||
}
|
||||
PRUint32 waitCount = service && pool && !databases.IsEmpty() ? 2 : 1;
|
||||
|
||||
bool
|
||||
IndexedDatabaseManager::IsClearOriginPending(const nsACString& origin)
|
||||
{
|
||||
// Iterate through our SynchronizedOps to see if we have an entry that matches
|
||||
// this origin and has no id.
|
||||
PRUint32 count = mSynchronizedOps.Length();
|
||||
for (PRUint32 index = 0; index < count; index++) {
|
||||
nsAutoPtr<SynchronizedOp>& op = mSynchronizedOps[index];
|
||||
if (op->mOrigin.Equals(origin) && !op->mId) {
|
||||
return true;
|
||||
nsRefPtr<WaitForTransactionsToFinishRunnable> runnable =
|
||||
new WaitForTransactionsToFinishRunnable(aOp, waitCount);
|
||||
|
||||
// There's no point in delaying if we don't yet have a transaction thread pool
|
||||
// or a file service. Also, if we're not waiting on any databases then we can
|
||||
// also run immediately.
|
||||
if (!(service || pool) || databases.IsEmpty()) {
|
||||
nsresult rv = runnable->Run();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Ask each service to call us back when they're done with this database.
|
||||
if (service) {
|
||||
// Have to copy here in case the pool needs a list too.
|
||||
nsTArray<nsCOMPtr<nsIFileStorage> > array;
|
||||
array.AppendElements(databases);
|
||||
|
||||
if (!service->WaitForAllStoragesToComplete(array, runnable)) {
|
||||
NS_WARNING("Failed to wait for storages to complete!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
if (pool && !pool->WaitForAllDatabasesToComplete(databases, runnable)) {
|
||||
NS_WARNING("Failed to wait for databases to complete!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS2(IndexedDatabaseManager, nsIIndexedDatabaseManager,
|
||||
@ -1237,33 +1232,31 @@ IndexedDatabaseManager::ClearDatabasesForURI(nsIURI* aURI)
|
||||
}
|
||||
|
||||
// Queue up the origin clear runnable.
|
||||
nsRefPtr<OriginClearRunnable> runnable =
|
||||
new OriginClearRunnable(origin, mIOThread);
|
||||
nsRefPtr<OriginClearRunnable> runnable = new OriginClearRunnable(origin);
|
||||
|
||||
rv = WaitForOpenAllowed(origin, nsnull, runnable);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Give the runnable some help by invalidating any databases in the way.
|
||||
// We need to grab references to any live databases here to prevent them from
|
||||
runnable->AdvanceState();
|
||||
|
||||
// Give the runnable some help by invalidating any databases in the way. We
|
||||
// need to grab references to any live databases here to prevent them from
|
||||
// dying while we invalidate them.
|
||||
nsTArray<nsRefPtr<IDBDatabase> > liveDatabases;
|
||||
|
||||
// Grab all live databases for this origin.
|
||||
nsTArray<IDBDatabase*>* array;
|
||||
if (mLiveDatabases.Get(origin, &array)) {
|
||||
liveDatabases.AppendElements(*array);
|
||||
}
|
||||
|
||||
// Invalidate all the live databases first.
|
||||
for (PRUint32 index = 0; index < liveDatabases.Length(); index++) {
|
||||
liveDatabases[index]->Invalidate();
|
||||
}
|
||||
|
||||
|
||||
DatabaseInfo::RemoveAllForOrigin(origin);
|
||||
|
||||
// After everything has been invalidated the helper should be dispatched to
|
||||
// the end of the event queue.
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -1374,60 +1367,105 @@ IndexedDatabaseManager::Observe(nsISupports* aSubject,
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS1(IndexedDatabaseManager::OriginClearRunnable,
|
||||
nsIRunnable)
|
||||
|
||||
// static
|
||||
void
|
||||
IndexedDatabaseManager::
|
||||
OriginClearRunnable::InvalidateOpenedDatabases(
|
||||
nsTArray<nsRefPtr<IDBDatabase> >& aDatabases,
|
||||
void* aClosure)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
OriginClearRunnable* self = static_cast<OriginClearRunnable*>(aClosure);
|
||||
|
||||
nsTArray<nsRefPtr<IDBDatabase> > databases;
|
||||
databases.SwapElements(aDatabases);
|
||||
|
||||
for (PRUint32 index = 0; index < databases.Length(); index++) {
|
||||
databases[index]->Invalidate();
|
||||
}
|
||||
|
||||
DatabaseInfo::RemoveAllForOrigin(self->mOrigin);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
IndexedDatabaseManager::OriginClearRunnable::Run()
|
||||
{
|
||||
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
|
||||
NS_ASSERTION(mgr, "This should never fail!");
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
// On the first time on the main thread we dispatch to the IO thread.
|
||||
if (mFirstCallback) {
|
||||
NS_ASSERTION(mThread, "Should have a thread here!");
|
||||
switch (mCallbackState) {
|
||||
case Pending: {
|
||||
NS_NOTREACHED("Should never get here without being dispatched!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
mFirstCallback = false;
|
||||
case OpenAllowed: {
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
nsCOMPtr<nsIThread> thread;
|
||||
mThread.swap(thread);
|
||||
AdvanceState();
|
||||
|
||||
// Dispatch to the IO thread.
|
||||
if (NS_FAILED(thread->Dispatch(this, NS_DISPATCH_NORMAL))) {
|
||||
NS_WARNING("Failed to dispatch to IO thread!");
|
||||
// Now we have to wait until the thread pool is done with all of the
|
||||
// databases we care about.
|
||||
nsresult rv =
|
||||
mgr->AcquireExclusiveAccess(mOrigin, this, InvalidateOpenedDatabases,
|
||||
this);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
case IO: {
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
AdvanceState();
|
||||
|
||||
// Remove the directory that contains all our databases.
|
||||
nsCOMPtr<nsIFile> directory;
|
||||
nsresult rv =
|
||||
mgr->GetDirectoryForOrigin(mOrigin, getter_AddRefs(directory));
|
||||
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to get directory to remove!");
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
bool exists;
|
||||
rv = directory->Exists(&exists);
|
||||
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
|
||||
"Failed to check that the directory exists!");
|
||||
|
||||
if (NS_SUCCEEDED(rv) && exists && NS_FAILED(directory->Remove(true))) {
|
||||
// This should never fail if we've closed all database connections
|
||||
// correctly...
|
||||
NS_ERROR("Failed to remove directory!");
|
||||
}
|
||||
}
|
||||
|
||||
// Now dispatch back to the main thread.
|
||||
if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) {
|
||||
NS_WARNING("Failed to dispatch to main thread!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_ASSERTION(!mThread, "Should have been cleared already!");
|
||||
case Complete: {
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
mgr->InvalidateFileManagersForOrigin(mOrigin);
|
||||
mgr->InvalidateFileManagersForOrigin(mOrigin);
|
||||
|
||||
// Tell the IndexedDatabaseManager that we're done.
|
||||
mgr->AllowNextSynchronizedOp(mOrigin, nsnull);
|
||||
// Tell the IndexedDatabaseManager that we're done.
|
||||
mgr->AllowNextSynchronizedOp(mOrigin, nsnull);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_ASSERTION(!mThread, "Should have been cleared already!");
|
||||
|
||||
// Remove the directory that contains all our databases.
|
||||
nsCOMPtr<nsIFile> directory;
|
||||
nsresult rv = mgr->GetDirectoryForOrigin(mOrigin, getter_AddRefs(directory));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
bool exists;
|
||||
rv = directory->Exists(&exists);
|
||||
if (NS_SUCCEEDED(rv) && exists) {
|
||||
rv = directory->Remove(true);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
default:
|
||||
NS_ERROR("Unknown state value!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to remove directory!");
|
||||
|
||||
// Switch back to the main thread to complete the sequence.
|
||||
rv = NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
NS_NOTREACHED("Should never get here!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
IndexedDatabaseManager::AsyncUsageRunnable::AsyncUsageRunnable(
|
||||
@ -1591,14 +1629,16 @@ IndexedDatabaseManager::AsyncUsageRunnable::Run()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS1(IndexedDatabaseManager::WaitForTransactionsToFinishRunnable,
|
||||
nsIRunnable)
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS1(
|
||||
IndexedDatabaseManager::WaitForTransactionsToFinishRunnable,
|
||||
nsIRunnable)
|
||||
|
||||
NS_IMETHODIMP
|
||||
IndexedDatabaseManager::WaitForTransactionsToFinishRunnable::Run()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ASSERTION(mOp && mOp->mHelper, "What?");
|
||||
NS_ASSERTION(mOp, "Null op!");
|
||||
NS_ASSERTION(mOp->mHelper || mOp->mRunnable, "Nothing to run!");
|
||||
NS_ASSERTION(mCountdown, "Wrong countdown!");
|
||||
|
||||
if (--mCountdown) {
|
||||
@ -1609,18 +1649,40 @@ IndexedDatabaseManager::WaitForTransactionsToFinishRunnable::Run()
|
||||
nsRefPtr<AsyncConnectionHelper> helper;
|
||||
helper.swap(mOp->mHelper);
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable;
|
||||
runnable.swap(mOp->mRunnable);
|
||||
|
||||
mOp = nsnull;
|
||||
|
||||
IndexedDatabaseManager::DispatchHelper(helper);
|
||||
nsresult rv;
|
||||
|
||||
// The helper is responsible for calling
|
||||
if (helper && helper->HasTransaction()) {
|
||||
// If the helper has a transaction, dispatch it to the transaction
|
||||
// threadpool.
|
||||
rv = helper->DispatchToTransactionPool();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
else {
|
||||
// Otherwise, dispatch it to the IO thread.
|
||||
IndexedDatabaseManager* manager = IndexedDatabaseManager::Get();
|
||||
NS_ASSERTION(manager, "We should definitely have a manager here");
|
||||
|
||||
nsIEventTarget* target = manager->IOThread();
|
||||
|
||||
rv = helper ?
|
||||
helper->Dispatch(target) :
|
||||
target->Dispatch(runnable, NS_DISPATCH_NORMAL);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// The helper or runnable is responsible for calling
|
||||
// IndexedDatabaseManager::AllowNextSynchronizedOp.
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS1(IndexedDatabaseManager::WaitForLockedFilesToFinishRunnable,
|
||||
nsIRunnable)
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS1(
|
||||
IndexedDatabaseManager::WaitForLockedFilesToFinishRunnable,
|
||||
nsIRunnable)
|
||||
|
||||
NS_IMETHODIMP
|
||||
IndexedDatabaseManager::WaitForLockedFilesToFinishRunnable::Run()
|
||||
@ -1632,8 +1694,9 @@ IndexedDatabaseManager::WaitForLockedFilesToFinishRunnable::Run()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
IndexedDatabaseManager::SynchronizedOp::SynchronizedOp(const nsACString& aOrigin,
|
||||
nsIAtom* aId)
|
||||
IndexedDatabaseManager::
|
||||
SynchronizedOp::SynchronizedOp(const nsACString& aOrigin,
|
||||
nsIAtom* aId)
|
||||
: mOrigin(aOrigin), mId(aId)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
@ -7,10 +7,7 @@
|
||||
#ifndef mozilla_dom_indexeddb_indexeddatabasemanager_h__
|
||||
#define mozilla_dom_indexeddb_indexeddatabasemanager_h__
|
||||
|
||||
#include "mozilla/dom/indexedDB/FileManager.h"
|
||||
#include "mozilla/dom/indexedDB/IndexedDatabase.h"
|
||||
#include "mozilla/dom/indexedDB/IDBDatabase.h"
|
||||
#include "mozilla/dom/indexedDB/IDBRequest.h"
|
||||
|
||||
#include "mozilla/Mutex.h"
|
||||
|
||||
@ -27,14 +24,17 @@
|
||||
#define INDEXEDDB_MANAGER_CONTRACTID "@mozilla.org/dom/indexeddb/manager;1"
|
||||
|
||||
class mozIStorageQuotaCallback;
|
||||
class nsIAtom;
|
||||
class nsIFile;
|
||||
class nsITimer;
|
||||
class nsPIDOMWindow;
|
||||
|
||||
BEGIN_INDEXEDDB_NAMESPACE
|
||||
|
||||
class AsyncConnectionHelper;
|
||||
|
||||
class CheckQuotaHelper;
|
||||
class FileManager;
|
||||
class IDBDatabase;
|
||||
|
||||
class IndexedDatabaseManager MOZ_FINAL : public nsIIndexedDatabaseManager,
|
||||
public nsIObserver
|
||||
@ -74,26 +74,29 @@ public:
|
||||
|
||||
static bool IsClosed();
|
||||
|
||||
typedef void (*WaitingOnDatabasesCallback)(nsTArray<nsRefPtr<IDBDatabase> >&, void*);
|
||||
typedef void
|
||||
(*WaitingOnDatabasesCallback)(nsTArray<nsRefPtr<IDBDatabase> >&, void*);
|
||||
|
||||
// Acquire exclusive access to the database given (waits for all others to
|
||||
// close). If databases need to close first, the callback will be invoked
|
||||
// with an array of said databases.
|
||||
nsresult AcquireExclusiveAccess(IDBDatabase* aDatabase,
|
||||
const nsACString& aOrigin,
|
||||
AsyncConnectionHelper* aHelper,
|
||||
WaitingOnDatabasesCallback aCallback,
|
||||
void* aClosure)
|
||||
{
|
||||
NS_ASSERTION(aDatabase, "Need a DB here!");
|
||||
return AcquireExclusiveAccess(aDatabase->Origin(), aDatabase, aHelper,
|
||||
return AcquireExclusiveAccess(aOrigin, aDatabase, aHelper, nsnull,
|
||||
aCallback, aClosure);
|
||||
}
|
||||
nsresult AcquireExclusiveAccess(const nsACString& aOrigin,
|
||||
AsyncConnectionHelper* aHelper,
|
||||
|
||||
nsresult AcquireExclusiveAccess(const nsACString& aOrigin,
|
||||
nsIRunnable* aRunnable,
|
||||
WaitingOnDatabasesCallback aCallback,
|
||||
void* aClosure)
|
||||
{
|
||||
return AcquireExclusiveAccess(aOrigin, nsnull, aHelper, aCallback,
|
||||
return AcquireExclusiveAccess(aOrigin, nsnull, nsnull, aRunnable, aCallback,
|
||||
aClosure);
|
||||
}
|
||||
|
||||
@ -197,9 +200,10 @@ private:
|
||||
IndexedDatabaseManager();
|
||||
~IndexedDatabaseManager();
|
||||
|
||||
nsresult AcquireExclusiveAccess(const nsACString& aOrigin,
|
||||
nsresult AcquireExclusiveAccess(const nsACString& aOrigin,
|
||||
IDBDatabase* aDatabase,
|
||||
AsyncConnectionHelper* aHelper,
|
||||
nsIRunnable* aRunnable,
|
||||
WaitingOnDatabasesCallback aCallback,
|
||||
void* aClosure);
|
||||
|
||||
@ -226,23 +230,54 @@ private:
|
||||
// IndexedDatabaseManager that the job has been completed.
|
||||
class OriginClearRunnable MOZ_FINAL : public nsIRunnable
|
||||
{
|
||||
enum CallbackState {
|
||||
// Not yet run.
|
||||
Pending = 0,
|
||||
|
||||
// Running on the main thread in the callback for OpenAllowed.
|
||||
OpenAllowed,
|
||||
|
||||
// Running on the IO thread.
|
||||
IO,
|
||||
|
||||
// Running on the main thread after all work is done.
|
||||
Complete
|
||||
};
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
OriginClearRunnable(const nsACString& aOrigin,
|
||||
nsIThread* aThread)
|
||||
OriginClearRunnable(const nsACString& aOrigin)
|
||||
: mOrigin(aOrigin),
|
||||
mThread(aThread),
|
||||
mFirstCallback(true)
|
||||
mCallbackState(Pending)
|
||||
{ }
|
||||
|
||||
nsCString mOrigin;
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
bool mFirstCallback;
|
||||
};
|
||||
void AdvanceState()
|
||||
{
|
||||
switch (mCallbackState) {
|
||||
case Pending:
|
||||
mCallbackState = OpenAllowed;
|
||||
return;
|
||||
case OpenAllowed:
|
||||
mCallbackState = IO;
|
||||
return;
|
||||
case IO:
|
||||
mCallbackState = Complete;
|
||||
return;
|
||||
default:
|
||||
NS_NOTREACHED("Can't advance past Complete!");
|
||||
}
|
||||
}
|
||||
|
||||
bool IsClearOriginPending(const nsACString& origin);
|
||||
static void InvalidateOpenedDatabases(
|
||||
nsTArray<nsRefPtr<IDBDatabase> >& aDatabases,
|
||||
void* aClosure);
|
||||
|
||||
private:
|
||||
nsCString mOrigin;
|
||||
CallbackState mCallbackState;
|
||||
};
|
||||
|
||||
// Responsible for calculating the amount of space taken up by databases of a
|
||||
// certain origin. Created when nsIIDBIndexedDatabaseManager::GetUsageForURI
|
||||
@ -301,6 +336,7 @@ private:
|
||||
const nsCString mOrigin;
|
||||
nsCOMPtr<nsIAtom> mId;
|
||||
nsRefPtr<AsyncConnectionHelper> mHelper;
|
||||
nsCOMPtr<nsIRunnable> mRunnable;
|
||||
nsTArray<nsCOMPtr<nsIRunnable> > mDelayedRunnables;
|
||||
nsTArray<nsRefPtr<IDBDatabase> > mDatabases;
|
||||
};
|
||||
@ -316,7 +352,8 @@ private:
|
||||
{
|
||||
NS_ASSERTION(mOp, "Why don't we have a runnable?");
|
||||
NS_ASSERTION(mOp->mDatabases.IsEmpty(), "We're here too early!");
|
||||
NS_ASSERTION(mOp->mHelper, "What are we supposed to do when we're done?");
|
||||
NS_ASSERTION(mOp->mHelper || mOp->mRunnable,
|
||||
"What are we supposed to do when we're done?");
|
||||
NS_ASSERTION(mCountdown, "Wrong countdown!");
|
||||
}
|
||||
|
||||
@ -361,7 +398,26 @@ private:
|
||||
nsString mFilePath;
|
||||
};
|
||||
|
||||
static nsresult DispatchHelper(AsyncConnectionHelper* aHelper);
|
||||
static nsresult RunSynchronizedOp(IDBDatabase* aDatabase,
|
||||
SynchronizedOp* aOp);
|
||||
|
||||
SynchronizedOp* FindSynchronizedOp(const nsACString& aOrigin,
|
||||
nsIAtom* aId)
|
||||
{
|
||||
for (PRUint32 index = 0; index < mSynchronizedOps.Length(); index++) {
|
||||
const nsAutoPtr<SynchronizedOp>& currentOp = mSynchronizedOps[index];
|
||||
if (currentOp->mOrigin == aOrigin &&
|
||||
(!currentOp->mId || currentOp->mId == aId)) {
|
||||
return currentOp;
|
||||
}
|
||||
}
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
bool IsClearOriginPending(const nsACString& aOrigin)
|
||||
{
|
||||
return !!FindSynchronizedOp(aOrigin, nsnull);
|
||||
}
|
||||
|
||||
// Maintains a list of live databases per origin.
|
||||
nsClassHashtable<nsCStringHashKey, nsTArray<IDBDatabase*> > mLiveDatabases;
|
||||
|
@ -1880,15 +1880,14 @@ OpenDatabaseHelper::StartSetVersion()
|
||||
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
|
||||
NS_ASSERTION(mgr, "This should never be null!");
|
||||
|
||||
rv = mgr->AcquireExclusiveAccess(mDatabase, helper,
|
||||
&VersionChangeEventsRunnable::QueueVersionChange<SetVersionHelper>,
|
||||
rv = mgr->AcquireExclusiveAccess(mDatabase, mDatabase->Origin(), helper,
|
||||
&VersionChangeEventsRunnable::QueueVersionChange<SetVersionHelper>,
|
||||
helper);
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
// The SetVersionHelper is responsible for dispatching us back to the
|
||||
// main thread again and changing the state to eSetVersionCompleted.
|
||||
mState = eSetVersionPending;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -1910,8 +1909,8 @@ OpenDatabaseHelper::StartDelete()
|
||||
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
|
||||
NS_ASSERTION(mgr, "This should never be null!");
|
||||
|
||||
rv = mgr->AcquireExclusiveAccess(mDatabase, helper,
|
||||
&VersionChangeEventsRunnable::QueueVersionChange<DeleteDatabaseHelper>,
|
||||
rv = mgr->AcquireExclusiveAccess(mDatabase, mDatabase->Origin(), helper,
|
||||
&VersionChangeEventsRunnable::QueueVersionChange<DeleteDatabaseHelper>,
|
||||
helper);
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
@ -2136,7 +2135,7 @@ OpenDatabaseHelper::NotifySetVersionFinished()
|
||||
NS_ASSERTION(mState = eSetVersionPending, "How did we get here?");
|
||||
|
||||
mState = eSetVersionCompleted;
|
||||
|
||||
|
||||
// Dispatch ourself back to the main thread
|
||||
return NS_DispatchToCurrentThread(this);
|
||||
}
|
||||
|
@ -104,6 +104,38 @@ public:
|
||||
DoDatabaseWork(mozIStorageConnection* aConnection) MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
class VersionChangeRunnable : public nsRunnable
|
||||
{
|
||||
nsRefPtr<IDBDatabase> mDatabase;
|
||||
uint64_t mOldVersion;
|
||||
uint64_t mNewVersion;
|
||||
|
||||
public:
|
||||
VersionChangeRunnable(IDBDatabase* aDatabase, const uint64_t& aOldVersion,
|
||||
const uint64_t& aNewVersion)
|
||||
: mDatabase(aDatabase), mOldVersion(aOldVersion), mNewVersion(aNewVersion)
|
||||
{
|
||||
MOZ_ASSERT(aDatabase);
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() MOZ_OVERRIDE
|
||||
{
|
||||
if (mDatabase->IsClosed()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsRefPtr<nsDOMEvent> event =
|
||||
IDBVersionChangeEvent::Create(mOldVersion, mNewVersion);
|
||||
MOZ_ASSERT(event);
|
||||
|
||||
bool dummy;
|
||||
nsresult rv = mDatabase->DispatchEvent(event, &dummy);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
/*******************************************************************************
|
||||
@ -362,12 +394,13 @@ IndexedDBDatabaseChild::RecvBlocked(const uint64_t& aOldVersion)
|
||||
MOZ_ASSERT(mRequest);
|
||||
MOZ_ASSERT(!mDatabase);
|
||||
|
||||
nsRefPtr<nsDOMEvent> event =
|
||||
IDBVersionChangeEvent::CreateBlocked(aOldVersion, mVersion);
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
IDBVersionChangeEvent::CreateBlockedRunnable(aOldVersion, mVersion,
|
||||
mRequest);
|
||||
|
||||
bool dummy;
|
||||
if (NS_FAILED(mRequest->DispatchEvent(event, &dummy))) {
|
||||
NS_WARNING("Failed to dispatch blocked event!");
|
||||
MainThreadEventTarget target;
|
||||
if (NS_FAILED(target.Dispatch(runnable, NS_DISPATCH_NORMAL))) {
|
||||
NS_WARNING("Dispatch of blocked event failed!");
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -379,16 +412,12 @@ IndexedDBDatabaseChild::RecvVersionChange(const uint64_t& aOldVersion,
|
||||
{
|
||||
MOZ_ASSERT(mDatabase);
|
||||
|
||||
if (mDatabase->IsClosed()) {
|
||||
return true;
|
||||
}
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
new VersionChangeRunnable(mDatabase, aOldVersion, aNewVersion);
|
||||
|
||||
nsRefPtr<nsDOMEvent> event =
|
||||
IDBVersionChangeEvent::Create(aOldVersion, aNewVersion);
|
||||
|
||||
bool dummy;
|
||||
if (NS_FAILED(mDatabase->DispatchEvent(event, &dummy))) {
|
||||
NS_WARNING("Failed to dispatch blocked event!");
|
||||
MainThreadEventTarget target;
|
||||
if (NS_FAILED(target.Dispatch(runnable, NS_DISPATCH_NORMAL))) {
|
||||
NS_WARNING("Dispatch of versionchange event failed!");
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -503,18 +532,7 @@ IndexedDBTransactionChild::SetTransaction(IDBTransaction* aTransaction)
|
||||
}
|
||||
|
||||
void
|
||||
IndexedDBTransactionChild::ActorDestroy(ActorDestroyReason aWhy)
|
||||
{
|
||||
if (mTransaction) {
|
||||
mTransaction->SetActor(static_cast<IndexedDBTransactionChild*>(NULL));
|
||||
#ifdef DEBUG
|
||||
mTransaction = NULL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
IndexedDBTransactionChild::RecvComplete(const nsresult& aRv)
|
||||
IndexedDBTransactionChild::FireCompleteEvent(nsresult aRv)
|
||||
{
|
||||
MOZ_ASSERT(mTransaction);
|
||||
MOZ_ASSERT(mStrongTransaction);
|
||||
@ -530,10 +548,36 @@ IndexedDBTransactionChild::RecvComplete(const nsresult& aRv)
|
||||
// }
|
||||
|
||||
nsRefPtr<CommitHelper> helper = new CommitHelper(transaction, aRv);
|
||||
if (NS_FAILED(helper->Run())) {
|
||||
NS_WARNING("CommitHelper failed!");
|
||||
|
||||
MainThreadEventTarget target;
|
||||
if (NS_FAILED(target.Dispatch(helper, NS_DISPATCH_NORMAL))) {
|
||||
NS_WARNING("Dispatch of CommitHelper failed!");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
IndexedDBTransactionChild::ActorDestroy(ActorDestroyReason aWhy)
|
||||
{
|
||||
if (mStrongTransaction) {
|
||||
// We're being torn down before we received a complete event from the parent
|
||||
// so fake one here.
|
||||
FireCompleteEvent(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
MOZ_ASSERT(!mStrongTransaction);
|
||||
}
|
||||
|
||||
if (mTransaction) {
|
||||
mTransaction->SetActor(static_cast<IndexedDBTransactionChild*>(NULL));
|
||||
#ifdef DEBUG
|
||||
mTransaction = NULL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
IndexedDBTransactionChild::RecvComplete(const nsresult& aRv)
|
||||
{
|
||||
FireCompleteEvent(aRv);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1059,12 +1103,13 @@ IndexedDBDeleteDatabaseRequestChild::RecvBlocked(
|
||||
{
|
||||
MOZ_ASSERT(mOpenRequest);
|
||||
|
||||
nsRefPtr<nsDOMEvent> event =
|
||||
IDBVersionChangeEvent::CreateBlocked(aCurrentVersion, 0);
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
IDBVersionChangeEvent::CreateBlockedRunnable(aCurrentVersion, 0,
|
||||
mOpenRequest);
|
||||
|
||||
bool dummy;
|
||||
if (NS_FAILED(mOpenRequest->DispatchEvent(event, &dummy))) {
|
||||
NS_WARNING("Failed to dispatch blocked event!");
|
||||
MainThreadEventTarget target;
|
||||
if (NS_FAILED(target.Dispatch(runnable, NS_DISPATCH_NORMAL))) {
|
||||
NS_WARNING("Dispatch of blocked event failed!");
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -155,6 +155,9 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
void
|
||||
FireCompleteEvent(nsresult aRv);
|
||||
|
||||
virtual void
|
||||
ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
|
||||
|
||||
|
@ -31,18 +31,14 @@ LOCAL_INCLUDES += \
|
||||
|
||||
DEFINES += -D_IMPL_NS_LAYOUT
|
||||
|
||||
# Test is disabled for the moment to investigate OS X 10.7 crash.
|
||||
#
|
||||
#TEST_FILES = \
|
||||
# test_ipc.html \
|
||||
# $(NULL)
|
||||
TEST_FILES = \
|
||||
test_ipc.html \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/config.mk
|
||||
include $(topsrcdir)/ipc/chromium/chromium-config.mk
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
# Test is disabled for the moment to investigate OS X 10.7 crash.
|
||||
#
|
||||
#libs:: $(TEST_FILES)
|
||||
# $(INSTALL) $(foreach f,$^,"$f") \
|
||||
# $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
|
||||
libs:: $(TEST_FILES)
|
||||
$(INSTALL) $(foreach f,$^,"$f") \
|
||||
$(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
|
||||
|
@ -12,7 +12,17 @@
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// This isn't a single test, really... It runs the entirety of the IndexedDB
|
||||
// tests. Each of those has a normal timeout handler, so there's no point in
|
||||
// having a timeout here. I'm setting this really high just to avoid getting
|
||||
// killed.
|
||||
SimpleTest.requestLongerTimeout(100);
|
||||
|
||||
function iframeScriptFirst() {
|
||||
// Disable this functionality, it breaks later tests.
|
||||
SpecialPowers.prototype.registerProcessCrashObservers = function() { };
|
||||
SpecialPowers.prototype.unregisterProcessCrashObservers = function() { };
|
||||
|
||||
content.wrappedJSObject.RunSet.reloadAndRunAll({
|
||||
preventDefault: function() { }
|
||||
});
|
||||
@ -41,7 +51,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
let regex = /^(TEST-PASS|TEST-UNEXPECTED-PASS|TEST-KNOWN-FAIL|TEST-UNEXPECTED-FAIL|TEST-DEBUG-INFO) \| ([^\|]+) \|(.*)/;
|
||||
let regexString =
|
||||
"^(TEST-PASS|TEST-UNEXPECTED-PASS|TEST-KNOWN-FAIL|TEST-UNEXPECTED-FAIL" +
|
||||
"|TEST-DEBUG-INFO) \\| ([^\\|]+) \\|(.*)";
|
||||
|
||||
let regex = new RegExp(regexString);
|
||||
|
||||
function onTestMessage(data) {
|
||||
let message = data.json.msg;
|
||||
@ -69,19 +83,8 @@
|
||||
}
|
||||
|
||||
function onTestComplete() {
|
||||
let comp = SpecialPowers.wrap(Components);
|
||||
|
||||
let idbManager =
|
||||
comp.classes["@mozilla.org/dom/indexeddb/manager;1"]
|
||||
.getService(comp.interfaces.nsIIndexedDatabaseManager);
|
||||
|
||||
let uri = SpecialPowers.getDocumentURIObject(document);
|
||||
idbManager.clearDatabasesForURI(uri);
|
||||
|
||||
idbManager.getUsageForURI(uri, function(uri, usage, fileUsage) {
|
||||
is(usage, 0, "Cleared ipc databases properly");
|
||||
SimpleTest.executeSoon(function () { SimpleTest.finish(); });
|
||||
});
|
||||
ok(true, "Got test complete message");
|
||||
SimpleTest.executeSoon(function () { SimpleTest.finish(); });
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
|
@ -15,7 +15,6 @@ XPCSHELL_TESTS = unit
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
|
||||
TEST_FILES = \
|
||||
bfcache_iframe1.html \
|
||||
bfcache_iframe2.html \
|
||||
|
@ -10,9 +10,39 @@ function executeSoon(aFun)
|
||||
SimpleTest.executeSoon(aFun);
|
||||
}
|
||||
|
||||
function clearAllDatabases(callback) {
|
||||
function runCallback() {
|
||||
SimpleTest.executeSoon(function () { callback(); });
|
||||
}
|
||||
|
||||
if (!SpecialPowers.isMainProcess()) {
|
||||
runCallback();
|
||||
return;
|
||||
}
|
||||
|
||||
let comp = SpecialPowers.wrap(Components);
|
||||
|
||||
let idbManager =
|
||||
comp.classes["@mozilla.org/dom/indexeddb/manager;1"]
|
||||
.getService(comp.interfaces.nsIIndexedDatabaseManager);
|
||||
|
||||
let uri = SpecialPowers.getDocumentURIObject(document);
|
||||
|
||||
idbManager.clearDatabasesForURI(uri);
|
||||
idbManager.getUsageForURI(uri, function(uri, usage, fileUsage) {
|
||||
if (usage) {
|
||||
throw new Error("getUsageForURI returned non-zero usage after " +
|
||||
"clearing all databases!");
|
||||
}
|
||||
runCallback();
|
||||
});
|
||||
}
|
||||
|
||||
if (!window.runTest) {
|
||||
window.runTest = function(limitedQuota)
|
||||
{
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
allowIndexedDB();
|
||||
if (limitedQuota) {
|
||||
denyUnlimitedQuota();
|
||||
@ -21,8 +51,7 @@ if (!window.runTest) {
|
||||
allowUnlimitedQuota();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
testGenerator.next();
|
||||
clearAllDatabases(function () { testGenerator.next(); });
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,7 +62,7 @@ function finishTest()
|
||||
|
||||
SimpleTest.executeSoon(function() {
|
||||
testGenerator.close();
|
||||
SimpleTest.finish();
|
||||
clearAllDatabases(function() { SimpleTest.finish(); });
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,98 @@
|
||||
<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_writer_starvation.js"></script>
|
||||
<script type="text/javascript;version=1.7">
|
||||
function testSteps()
|
||||
{
|
||||
const name = window.location.pathname;
|
||||
|
||||
// Needs to be enough to saturate the thread pool.
|
||||
const SYNC_REQUEST_COUNT = 25;
|
||||
|
||||
let request = mozIndexedDB.open(name, 1);
|
||||
request.onerror = errorHandler;
|
||||
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
let event = yield;
|
||||
|
||||
let db = event.target.result;
|
||||
db.onerror = errorHandler;
|
||||
|
||||
is(event.target.transaction.mode, "versionchange", "Correct mode");
|
||||
|
||||
let objectStore = db.createObjectStore("foo", { autoIncrement: true });
|
||||
|
||||
request = objectStore.add({});
|
||||
request.onerror = errorHandler;
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
let key = event.target.result;
|
||||
ok(key, "Got a key");
|
||||
|
||||
yield;
|
||||
|
||||
let continueReading = true;
|
||||
let readerCount = 0;
|
||||
let writerCount = 0;
|
||||
let callbackCount = 0;
|
||||
|
||||
// Generate a bunch of reads right away without returning to the event
|
||||
// loop.
|
||||
info("Generating " + SYNC_REQUEST_COUNT + " readonly requests");
|
||||
|
||||
for (let i = 0; i < SYNC_REQUEST_COUNT; i++) {
|
||||
readerCount++;
|
||||
let request = db.transaction("foo").objectStore("foo").get(key);
|
||||
request.onsuccess = function(event) {
|
||||
is(event.target.transaction.mode, "readonly", "Correct mode");
|
||||
callbackCount++;
|
||||
};
|
||||
}
|
||||
|
||||
while (continueReading) {
|
||||
readerCount++;
|
||||
info("Generating additional readonly request (" + readerCount + ")");
|
||||
let request = db.transaction("foo").objectStore("foo").get(key);
|
||||
request.onsuccess = function(event) {
|
||||
callbackCount++;
|
||||
info("Received readonly request callback (" + callbackCount + ")");
|
||||
is(event.target.transaction.mode, "readonly", "Correct mode");
|
||||
if (callbackCount == SYNC_REQUEST_COUNT) {
|
||||
writerCount++;
|
||||
info("Generating 1 readwrite request with " + readerCount +
|
||||
" previous readonly requests");
|
||||
let request = db.transaction("foo", "readwrite")
|
||||
.objectStore("foo")
|
||||
.add({}, readerCount);
|
||||
request.onsuccess = function(event) {
|
||||
callbackCount++;
|
||||
info("Received readwrite request callback (" + callbackCount + ")");
|
||||
is(event.target.transaction.mode, "readwrite", "Correct mode");
|
||||
is(event.target.result, callbackCount,
|
||||
"write callback came before later reads");
|
||||
}
|
||||
}
|
||||
else if (callbackCount == SYNC_REQUEST_COUNT + 5) {
|
||||
continueReading = false;
|
||||
}
|
||||
};
|
||||
|
||||
setTimeout(function() { testGenerator.next(); }, writerCount ? 1000 : 100);
|
||||
yield;
|
||||
}
|
||||
|
||||
while (callbackCount < (readerCount + writerCount)) {
|
||||
executeSoon(function() { testGenerator.next(); });
|
||||
yield;
|
||||
}
|
||||
|
||||
is(callbackCount, readerCount + writerCount, "All requests accounted for");
|
||||
|
||||
finishTest();
|
||||
yield;
|
||||
}
|
||||
</script>
|
||||
<script type="text/javascript;version=1.7" src="helpers.js"></script>
|
||||
|
||||
</head>
|
||||
|
@ -62,10 +62,8 @@ TEST_FILES = \
|
||||
test_transaction_lifetimes.js \
|
||||
test_transaction_lifetimes_nested.js \
|
||||
test_transaction_ordering.js \
|
||||
test_writer_starvation.js \
|
||||
$(NULL)
|
||||
|
||||
|
||||
libs:: $(TEST_FILES)
|
||||
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
|
||||
|
||||
|
@ -57,8 +57,8 @@ function testSteps()
|
||||
ok(!(event.newVersion === 0), "newVersion should be null");
|
||||
db.close();
|
||||
db2.close();
|
||||
db.onversionchange = errorHandler;
|
||||
db2.onversionchange = errorHandler;
|
||||
db.onversionchange = unexpectedSuccessHandler;
|
||||
db2.onversionchange = unexpectedSuccessHandler;
|
||||
};
|
||||
|
||||
// The IDB spec doesn't guarantee the order that onversionchange will fire
|
||||
|
@ -11,7 +11,7 @@ function testSteps()
|
||||
const description = "My Test Database";
|
||||
|
||||
let request = mozIndexedDB.open(name, 1, description);
|
||||
request.onerror = grabEventAndContinueHandler;
|
||||
request.onerror = errorHandler;
|
||||
request.onsuccess = unexpectedSuccessHandler;
|
||||
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||
let event = yield;
|
||||
@ -38,7 +38,11 @@ function testSteps()
|
||||
is(db.version, 1, "Correct version");
|
||||
is(db.objectStoreNames.length, 1, "Correct objectStoreNames length");
|
||||
|
||||
request.onerror = grabEventAndContinueHandler;
|
||||
request.onupgradeneeded = unexpectedSuccessHandler;
|
||||
|
||||
event = yield;
|
||||
|
||||
is(event.type, "error", "Got request error event");
|
||||
is(event.target, request, "Right target");
|
||||
is(event.target.transaction, null, "No transaction");
|
||||
@ -46,19 +50,27 @@ function testSteps()
|
||||
event.preventDefault();
|
||||
|
||||
request = mozIndexedDB.open(name, 1, description);
|
||||
request.onerror = grabEventAndContinueHandler;
|
||||
request.onerror = errorHandler;
|
||||
request.onsuccess = unexpectedSuccessHandler;
|
||||
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||
let event = yield;
|
||||
event = yield;
|
||||
|
||||
is(event.type, "upgradeneeded", "Got upgradeneeded event");
|
||||
|
||||
let db2 = event.target.result;
|
||||
|
||||
|
||||
isnot(db, db2, "Should give a different db instance");
|
||||
is(db2.version, 1, "Correct version");
|
||||
is(db2.objectStoreNames.length, 0, "Correct objectStoreNames length");
|
||||
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
yield;
|
||||
request.onupgradeneeded = unexpectedSuccessHandler;
|
||||
event = yield;
|
||||
|
||||
is(event.target.result, db2, "Correct target");
|
||||
is(event.type, "success", "Got success event");
|
||||
is(db2.version, 1, "Correct version");
|
||||
is(db2.objectStoreNames.length, 0, "Correct objectStoreNames length");
|
||||
|
||||
finishTest();
|
||||
yield;
|
||||
|
@ -17,6 +17,7 @@ function testSteps()
|
||||
let request2 = mozIndexedDB.open(name, 2);
|
||||
request2.onerror = errorHandler;
|
||||
request2.onupgradeneeded = unexpectedSuccessHandler;
|
||||
request2.onsuccess = unexpectedSuccessHandler;
|
||||
|
||||
let event = yield;
|
||||
is(event.type, "upgradeneeded", "Expect an upgradeneeded event");
|
||||
@ -41,9 +42,11 @@ function testSteps()
|
||||
is(e.code, DOMException.INVALID_STATE_ERR, "Expect an INVALID_STATE_ERR");
|
||||
}
|
||||
|
||||
request.onupgradeneeded = unexpectedSuccessHandler;
|
||||
request.transaction.oncomplete = grabEventAndContinueHandler;
|
||||
|
||||
yield;
|
||||
event = yield;
|
||||
is(event.type, "complete", "Got complete event");
|
||||
|
||||
// The database is still not fully open here.
|
||||
try {
|
||||
@ -57,7 +60,9 @@ function testSteps()
|
||||
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
|
||||
yield;
|
||||
event = yield;
|
||||
is(event.type, "success", "Expect a success event");
|
||||
is(event.target.result, db, "Same database");
|
||||
|
||||
db.onversionchange = function() {
|
||||
ok(true, "next setVersion was unblocked appropriately");
|
||||
@ -71,10 +76,22 @@ function testSteps()
|
||||
ok(false, "Transactions should be allowed now!");
|
||||
}
|
||||
|
||||
request2.onupgradeneeded = null;
|
||||
request.onsuccess = unexpectedSuccessHandler;
|
||||
request2.onupgradeneeded = grabEventAndContinueHandler;
|
||||
|
||||
event = yield;
|
||||
is(event.type, "upgradeneeded", "Expect an upgradeneeded event");
|
||||
|
||||
db = event.target.result;
|
||||
is(db.version, 2, "Database has correct version");
|
||||
|
||||
request2.onupgradeneeded = unexpectedSuccessHandler;
|
||||
request2.onsuccess = grabEventAndContinueHandler;
|
||||
|
||||
yield;
|
||||
event = yield;
|
||||
is(event.type, "success", "Expect a success event");
|
||||
is(event.target.result, db, "Same database");
|
||||
is(db.version, 2, "Database has correct version");
|
||||
|
||||
finishTest();
|
||||
yield;
|
||||
|
@ -1,90 +0,0 @@
|
||||
/**
|
||||
* 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 description = "My Test Database";
|
||||
|
||||
let request = mozIndexedDB.open(name, 1, description);
|
||||
request.onerror = errorHandler;
|
||||
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
let event = yield;
|
||||
|
||||
let db = event.target.result;
|
||||
|
||||
is(event.target.transaction.mode, "versionchange", "Correct mode");
|
||||
|
||||
let objectStore = db.createObjectStore("foo", { autoIncrement: true });
|
||||
|
||||
request = objectStore.add({});
|
||||
request.onerror = errorHandler;
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
let key = event.target.result;
|
||||
ok(key, "Got a key");
|
||||
|
||||
yield;
|
||||
|
||||
let continueReading = true;
|
||||
let readerCount = 0;
|
||||
let callbackCount = 0;
|
||||
let finalCallbackCount = 0;
|
||||
|
||||
// Generate a bunch of reads right away without returning to the event
|
||||
// loop.
|
||||
for (let i = 0; i < 20; i++) {
|
||||
readerCount++;
|
||||
request = db.transaction("foo").objectStore("foo").get(key);
|
||||
request.onerror = errorHandler;
|
||||
request.onsuccess = function(event) {
|
||||
callbackCount++;
|
||||
};
|
||||
}
|
||||
|
||||
while (continueReading) {
|
||||
readerCount++;
|
||||
request = db.transaction("foo").objectStore("foo").get(key);
|
||||
request.onerror = errorHandler;
|
||||
request.onsuccess = function(event) {
|
||||
is(event.target.transaction.mode, "readonly", "Correct mode");
|
||||
callbackCount++;
|
||||
if (callbackCount == 100) {
|
||||
request = db.transaction("foo", "readwrite")
|
||||
.objectStore("foo")
|
||||
.add({}, readerCount);
|
||||
request.onerror = errorHandler;
|
||||
request.onsuccess = function(event) {
|
||||
continueReading = false;
|
||||
finalCallbackCount = callbackCount;
|
||||
is(event.target.result, callbackCount,
|
||||
"write callback came before later reads");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
executeSoon(function() { testGenerator.next(); });
|
||||
yield;
|
||||
}
|
||||
|
||||
while (callbackCount < readerCount) {
|
||||
executeSoon(function() { testGenerator.next(); });
|
||||
yield;
|
||||
}
|
||||
|
||||
is(callbackCount, readerCount, "All requests accounted for");
|
||||
ok(callbackCount > finalCallbackCount, "More readers after writer");
|
||||
|
||||
finishTest();
|
||||
yield;
|
||||
}
|
||||
|
||||
if (this.window)
|
||||
SimpleTest.requestLongerTimeout(5); // see bug 580875
|
||||
|
@ -51,5 +51,4 @@ tail =
|
||||
[test_transaction_abort.js]
|
||||
[test_transaction_lifetimes.js]
|
||||
[test_transaction_lifetimes_nested.js]
|
||||
[test_transaction_ordering.js]
|
||||
[test_writer_starvation.js]
|
||||
[test_transaction_ordering.js]
|
Loading…
Reference in New Issue
Block a user