mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-14 10:43:24 +00:00
Bug 608186 - 'IndexedDB: Transactions should expire when we return to the event loop'. r=sicking, a=blocking+
This commit is contained in:
parent
f6c175b300
commit
488481b966
@ -145,8 +145,7 @@ AsyncConnectionHelper::Run()
|
||||
mRequest->SetDone();
|
||||
}
|
||||
|
||||
NS_ASSERTION(!gCurrentTransaction, "Should be null!");
|
||||
gCurrentTransaction = mTransaction;
|
||||
SetCurrentTransaction(mTransaction);
|
||||
|
||||
// Call OnError if the database had an error or if the OnSuccess handler
|
||||
// has an error.
|
||||
@ -155,8 +154,9 @@ AsyncConnectionHelper::Run()
|
||||
OnError(mRequest, mResultCode);
|
||||
}
|
||||
|
||||
NS_ASSERTION(gCurrentTransaction == mTransaction, "Should be unchanged!");
|
||||
gCurrentTransaction = nsnull;
|
||||
NS_ASSERTION(GetCurrentTransaction() == mTransaction,
|
||||
"Should be unchanged!");
|
||||
SetCurrentTransaction(nsnull);
|
||||
|
||||
if (mDispatched && mTransaction) {
|
||||
mTransaction->OnRequestFinished();
|
||||
@ -319,6 +319,20 @@ AsyncConnectionHelper::GetCurrentTransaction()
|
||||
return gCurrentTransaction;
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
AsyncConnectionHelper::SetCurrentTransaction(IDBTransaction* aTransaction)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
if (aTransaction) {
|
||||
NS_ASSERTION(!gCurrentTransaction, "Overwriting current transaction!");
|
||||
}
|
||||
|
||||
gCurrentTransaction = aTransaction;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
AsyncConnectionHelper::Init()
|
||||
{
|
||||
|
@ -87,6 +87,7 @@ public:
|
||||
}
|
||||
|
||||
static IDBTransaction* GetCurrentTransaction();
|
||||
static void SetCurrentTransaction(IDBTransaction* aTransaction);
|
||||
|
||||
nsISupports* GetSource()
|
||||
{
|
||||
|
@ -811,9 +811,15 @@ ContinueRunnable::Run()
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
AsyncConnectionHelper::SetCurrentTransaction(cursor->mTransaction);
|
||||
|
||||
PRBool dummy;
|
||||
cursor->mRequest->DispatchEvent(event, &dummy);
|
||||
|
||||
NS_ASSERTION(AsyncConnectionHelper::GetCurrentTransaction() ==
|
||||
cursor->mTransaction, "Should be unchanged!");
|
||||
AsyncConnectionHelper::SetCurrentTransaction(nsnull);
|
||||
|
||||
cursor->mTransaction->OnRequestFinished();
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -636,7 +636,7 @@ IDBDatabase::SetVersion(const nsAString& aVersion,
|
||||
nsTArray<nsString> storesToOpen;
|
||||
nsRefPtr<IDBTransaction> transaction =
|
||||
IDBTransaction::Create(this, storesToOpen, IDBTransaction::VERSION_CHANGE,
|
||||
kDefaultDatabaseTimeoutSeconds);
|
||||
kDefaultDatabaseTimeoutSeconds, true);
|
||||
NS_ENSURE_TRUE(transaction, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
nsRefPtr<IDBVersionChangeRequest> request =
|
||||
|
@ -48,6 +48,7 @@
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#include "AsyncConnectionHelper.h"
|
||||
#include "DatabaseInfo.h"
|
||||
#include "IDBCursor.h"
|
||||
#include "IDBEvents.h"
|
||||
@ -61,6 +62,8 @@ USING_INDEXEDDB_NAMESPACE
|
||||
|
||||
namespace {
|
||||
|
||||
IDBTransaction::ThreadObserver* gThreadObserver = nsnull;
|
||||
|
||||
PLDHashOperator
|
||||
DoomCachedStatements(const nsACString& aQuery,
|
||||
nsCOMPtr<mozIStorageStatement>& aStatement,
|
||||
@ -78,7 +81,8 @@ already_AddRefed<IDBTransaction>
|
||||
IDBTransaction::Create(IDBDatabase* aDatabase,
|
||||
nsTArray<nsString>& aObjectStoreNames,
|
||||
PRUint16 aMode,
|
||||
PRUint32 aTimeout)
|
||||
PRUint32 aTimeout,
|
||||
bool aDispatchDelayed)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
@ -101,6 +105,13 @@ IDBTransaction::Create(IDBDatabase* aDatabase,
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
if (!aDispatchDelayed) {
|
||||
if (!ThreadObserver::BeginObserving(transaction)) {
|
||||
return nsnull;
|
||||
}
|
||||
transaction->mCreating = true;
|
||||
}
|
||||
|
||||
return transaction.forget();
|
||||
}
|
||||
|
||||
@ -111,7 +122,7 @@ IDBTransaction::IDBTransaction()
|
||||
mPendingRequests(0),
|
||||
mSavepointCount(0),
|
||||
mAborted(false),
|
||||
mClosed(false)
|
||||
mCreating(false)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
}
|
||||
@ -122,6 +133,7 @@ IDBTransaction::~IDBTransaction()
|
||||
NS_ASSERTION(!mPendingRequests, "Should have no pending requests here!");
|
||||
NS_ASSERTION(!mSavepointCount, "Should have released them all!");
|
||||
NS_ASSERTION(!mConnection, "Should have called CommitOrRollback!");
|
||||
NS_ASSERTION(!mCreating, "Should have been cleared already!");
|
||||
|
||||
if (mListenerManager) {
|
||||
mListenerManager->Disconnect();
|
||||
@ -150,10 +162,6 @@ IDBTransaction::OnRequestFinished()
|
||||
if (!mAborted) {
|
||||
NS_ASSERTION(mReadyState == nsIIDBTransaction::LOADING, "Bad state!");
|
||||
}
|
||||
|
||||
NS_ASSERTION(!mClosed, "Shouldn't be closed yet!");
|
||||
mClosed = true;
|
||||
|
||||
CommitOrRollback();
|
||||
}
|
||||
}
|
||||
@ -533,16 +541,35 @@ IDBTransaction::GetCachedStatement(const nsACString& aQuery)
|
||||
return stmt.forget();
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
bool
|
||||
IDBTransaction::TransactionIsOpen() const
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
return (mReadyState == nsIIDBTransaction::INITIAL ||
|
||||
mReadyState == nsIIDBTransaction::LOADING) &&
|
||||
!mClosed;
|
||||
|
||||
// If we haven't started anything then we're open.
|
||||
if (mReadyState == nsIIDBTransaction::INITIAL) {
|
||||
NS_ASSERTION(AsyncConnectionHelper::GetCurrentTransaction() != this,
|
||||
"This should be some other transaction (or null)!");
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we've already started then we need to check to see if we still have the
|
||||
// mCreating flag set. If we do (i.e. we haven't returned to the event loop
|
||||
// from the time we were created) then we are open. Otherwise check the
|
||||
// currently running transaction to see if it's the same. We only allow other
|
||||
// requests to be made if this transaction is currently running.
|
||||
if (mReadyState == nsIIDBTransaction::LOADING) {
|
||||
if (mCreating) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (AsyncConnectionHelper::GetCurrentTransaction() == this) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
already_AddRefed<IDBObjectStore>
|
||||
IDBTransaction::GetOrCreateObjectStore(const nsAString& aName,
|
||||
@ -792,6 +819,234 @@ IDBTransaction::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
IDBTransaction::
|
||||
ThreadObserver::ThreadObserver()
|
||||
: mBaseRecursionDepth(0),
|
||||
mDone(false)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ASSERTION(!gThreadObserver, "Multiple observers?!");
|
||||
}
|
||||
|
||||
IDBTransaction::
|
||||
ThreadObserver::~ThreadObserver()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ASSERTION(gThreadObserver == this, "Multiple observers?!");
|
||||
|
||||
#ifdef DEBUG
|
||||
for (PRUint32 i = 0; i < mTransactions.Length(); i++) {
|
||||
NS_ASSERTION(mTransactions[i].transactions.IsEmpty(),
|
||||
"Unprocessed transactions!");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Clear the global.
|
||||
gThreadObserver = nsnull;
|
||||
}
|
||||
|
||||
void
|
||||
IDBTransaction::
|
||||
ThreadObserver::UpdateNewlyCreatedTransactions(PRUint32 aRecursionDepth)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
for (PRUint32 i = 0; i < mTransactions.Length(); i++) {
|
||||
TransactionInfo& info = mTransactions[i];
|
||||
|
||||
if (info.recursionDepth == aRecursionDepth) {
|
||||
for (PRUint32 j = 0; j < info.transactions.Length(); j++) {
|
||||
nsRefPtr<IDBTransaction>& transaction = info.transactions[j];
|
||||
|
||||
// Clear the mCreating flag now.
|
||||
transaction->mCreating = false;
|
||||
|
||||
// And maybe set the readyState to DONE if there were no requests
|
||||
// generated.
|
||||
if (transaction->mReadyState == nsIIDBTransaction::INITIAL) {
|
||||
transaction->mReadyState = nsIIDBTransaction::DONE;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't hang on to transactions any longer than we have to.
|
||||
info.transactions.Clear();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
IDBTransaction::
|
||||
ThreadObserver::BeginObserving(IDBTransaction* aTransaction)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ASSERTION(aTransaction, "Null pointer!");
|
||||
|
||||
nsCOMPtr<nsIThreadInternal2> thread(do_QueryInterface(NS_GetCurrentThread()));
|
||||
NS_ENSURE_TRUE(thread, false);
|
||||
|
||||
// We need the current recursion depth first.
|
||||
PRUint32 depth;
|
||||
nsresult rv = thread->GetRecursionDepth(&depth);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
NS_ASSERTION(depth, "This should never be 0!");
|
||||
depth--;
|
||||
|
||||
// If we've already got an observer created then simply append this
|
||||
// transaction to its list.
|
||||
if (gThreadObserver) {
|
||||
for (PRUint32 i = 0; i < gThreadObserver->mTransactions.Length(); i++) {
|
||||
TransactionInfo& info = gThreadObserver->mTransactions[i];
|
||||
if (info.recursionDepth == depth) {
|
||||
if (!info.transactions.AppendElement(aTransaction)) {
|
||||
NS_WARNING("Out of memory!");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// No transactions at this depth yet, make a new entry
|
||||
TransactionInfo* newInfo = gThreadObserver->mTransactions.AppendElement();
|
||||
if (!newInfo || !newInfo->transactions.AppendElement(aTransaction)) {
|
||||
NS_WARNING("Out of memory!");
|
||||
return false;
|
||||
}
|
||||
newInfo->recursionDepth = depth;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Make a new thread observer and install it.
|
||||
nsRefPtr<ThreadObserver> observer(new ThreadObserver());
|
||||
|
||||
TransactionInfo* info = observer->mTransactions.AppendElement();
|
||||
NS_ASSERTION(info, "This should never fail!");
|
||||
|
||||
info->recursionDepth = observer->mBaseRecursionDepth = depth;
|
||||
|
||||
if (!info->transactions.AppendElement(aTransaction)) {
|
||||
NS_WARNING("Out of memory!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// We need to keep the thread observer chain intact so grab the previous
|
||||
// observer.
|
||||
rv = thread->GetObserver(getter_AddRefs(observer->mPreviousObserver));
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
// Now set our new observer.
|
||||
rv = thread->SetObserver(observer);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
// And set the global so that we don't recreate it later.
|
||||
gThreadObserver = observer;
|
||||
return true;
|
||||
}
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS1(IDBTransaction::ThreadObserver, nsIThreadObserver)
|
||||
|
||||
NS_IMETHODIMP
|
||||
IDBTransaction::
|
||||
ThreadObserver::OnDispatchedEvent(nsIThreadInternal* aThread)
|
||||
{
|
||||
// This may be called on any thread!
|
||||
|
||||
// Nothing special is needed here, just call the previous observer.
|
||||
if (mPreviousObserver) {
|
||||
return mPreviousObserver->OnDispatchedEvent(aThread);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
IDBTransaction::
|
||||
ThreadObserver::OnProcessNextEvent(nsIThreadInternal* aThread,
|
||||
PRBool aMayWait,
|
||||
PRUint32 aRecursionDepth)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ASSERTION(aThread, "This should never be null!");
|
||||
NS_ASSERTION(!mKungFuDeathGrip, "Shouldn't have a self-ref here!");
|
||||
|
||||
// If we're at the base recursion depth here then we're ready to unset
|
||||
// ourselves as the thread observer.
|
||||
if (aRecursionDepth == mBaseRecursionDepth || mDone) {
|
||||
// From here on we'll continue to try to unset ourselves.
|
||||
mDone = true;
|
||||
|
||||
nsCOMPtr<nsIThreadObserver> currentObserver;
|
||||
if (NS_FAILED(aThread->GetObserver(getter_AddRefs(currentObserver)))) {
|
||||
NS_WARNING("Can't get current observer?!");
|
||||
}
|
||||
|
||||
// We can only set the previous observer if this is the current observer.
|
||||
// Otherwise someone else has installed themselves into the chain and we
|
||||
// have to hang around until they unset themselves.
|
||||
if (currentObserver == this) {
|
||||
// Setting a different thread observer could delete us. Maintain a
|
||||
// reference until AfterProcessNextEvent is called.
|
||||
mKungFuDeathGrip = this;
|
||||
|
||||
// Set our previous observer back on the thread.
|
||||
if (NS_FAILED(aThread->SetObserver(mPreviousObserver))) {
|
||||
NS_ERROR("This should never fail!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Take care of any transactions that were created at this recursion depth.
|
||||
UpdateNewlyCreatedTransactions(aRecursionDepth);
|
||||
|
||||
// And call the previous observer.
|
||||
if (mPreviousObserver) {
|
||||
return mPreviousObserver->OnProcessNextEvent(aThread, aMayWait,
|
||||
aRecursionDepth);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
IDBTransaction::
|
||||
ThreadObserver::AfterProcessNextEvent(nsIThreadInternal* aThread,
|
||||
PRUint32 aRecursionDepth)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ASSERTION(aThread, "This should never be null!");
|
||||
|
||||
nsRefPtr<ThreadObserver> kungFuDeathGrip;
|
||||
nsCOMPtr<nsIThreadObserver> observer;
|
||||
|
||||
if (mKungFuDeathGrip) {
|
||||
NS_ASSERTION(mDone, "Huh?!");
|
||||
|
||||
// We can drop the reference to this observer after this call.
|
||||
kungFuDeathGrip.swap(mKungFuDeathGrip);
|
||||
|
||||
// And we don't need the previous observer after this call either.
|
||||
observer.swap(mPreviousObserver);
|
||||
}
|
||||
else {
|
||||
// Still call the previous observer.
|
||||
observer = mPreviousObserver;
|
||||
}
|
||||
|
||||
// We may have collected more transactions while the event was processed.
|
||||
// Update them now.
|
||||
UpdateNewlyCreatedTransactions(aRecursionDepth);
|
||||
|
||||
if (observer) {
|
||||
return observer->AfterProcessNextEvent(aThread, aRecursionDepth);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
CommitHelper::CommitHelper(IDBTransaction* aTransaction)
|
||||
: mTransaction(aTransaction),
|
||||
mAborted(!!aTransaction->mAborted),
|
||||
@ -809,8 +1064,6 @@ NS_IMPL_THREADSAFE_ISUPPORTS1(CommitHelper, nsIRunnable)
|
||||
NS_IMETHODIMP
|
||||
CommitHelper::Run()
|
||||
{
|
||||
NS_ASSERTION(mTransaction->mClosed, "Should be closed!");
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
NS_ASSERTION(mDoomedObjects.IsEmpty(), "Didn't release doomed objects!");
|
||||
|
||||
|
@ -45,6 +45,7 @@
|
||||
|
||||
#include "nsIIDBTransaction.h"
|
||||
#include "nsIRunnable.h"
|
||||
#include "nsIThreadInternal.h"
|
||||
|
||||
#include "nsDOMEventTargetHelper.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
@ -69,6 +70,7 @@ class IDBTransaction : public nsDOMEventTargetHelper,
|
||||
{
|
||||
friend class AsyncConnectionHelper;
|
||||
friend class CommitHelper;
|
||||
friend class ThreadObserver;
|
||||
friend class TransactionThreadPool;
|
||||
|
||||
public:
|
||||
@ -82,7 +84,8 @@ public:
|
||||
Create(IDBDatabase* aDatabase,
|
||||
nsTArray<nsString>& aObjectStoreNames,
|
||||
PRUint16 aMode,
|
||||
PRUint32 aTimeout);
|
||||
PRUint32 aTimeout,
|
||||
bool aDispatchDelayed = false);
|
||||
|
||||
// nsPIDOMEventTarget
|
||||
virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor);
|
||||
@ -133,16 +136,7 @@ public:
|
||||
return GetCachedStatement(query);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
bool TransactionIsOpen() const;
|
||||
#else
|
||||
bool TransactionIsOpen() const
|
||||
{
|
||||
return (mReadyState == nsIIDBTransaction::INITIAL ||
|
||||
mReadyState == nsIIDBTransaction::LOADING) &&
|
||||
!mClosed;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool IsWriteAllowed() const
|
||||
{
|
||||
@ -165,6 +159,35 @@ public:
|
||||
GetOrCreateObjectStore(const nsAString& aName,
|
||||
ObjectStoreInfo* aObjectStoreInfo);
|
||||
|
||||
class ThreadObserver : public nsIThreadObserver
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSITHREADOBSERVER
|
||||
|
||||
static bool BeginObserving(IDBTransaction* aTransaction);
|
||||
|
||||
private:
|
||||
ThreadObserver();
|
||||
~ThreadObserver();
|
||||
|
||||
void UpdateNewlyCreatedTransactions(PRUint32 aRecursionDepth);
|
||||
|
||||
struct TransactionInfo
|
||||
{
|
||||
PRUint32 recursionDepth;
|
||||
nsTArray<nsRefPtr<IDBTransaction> > transactions;
|
||||
};
|
||||
|
||||
nsAutoTArray<TransactionInfo, 1> mTransactions;
|
||||
|
||||
nsCOMPtr<nsIThreadObserver> mPreviousObserver;
|
||||
nsRefPtr<ThreadObserver> mKungFuDeathGrip;
|
||||
|
||||
PRUint32 mBaseRecursionDepth;
|
||||
bool mDone;
|
||||
};
|
||||
|
||||
private:
|
||||
IDBTransaction();
|
||||
~IDBTransaction();
|
||||
@ -196,7 +219,7 @@ private:
|
||||
nsTArray<nsRefPtr<IDBObjectStore> > mCreatedObjectStores;
|
||||
|
||||
bool mAborted;
|
||||
bool mClosed;
|
||||
bool mCreating;
|
||||
};
|
||||
|
||||
class CommitHelper : public nsIRunnable
|
||||
|
@ -78,6 +78,8 @@ TEST_FILES = \
|
||||
test_remove_objectStore.html \
|
||||
test_request_readyState.html \
|
||||
test_transaction_abort.html \
|
||||
test_transaction_lifetimes.html \
|
||||
test_transaction_lifetimes_nested.html \
|
||||
test_setVersion.html \
|
||||
test_setVersion_abort.html \
|
||||
test_setVersion_events.html \
|
||||
|
55
dom/indexedDB/test/test_transaction_lifetimes.html
Normal file
55
dom/indexedDB/test/test_transaction_lifetimes.html
Normal file
@ -0,0 +1,55 @@
|
||||
<!--
|
||||
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="/MochiKit/packed.js"></script>
|
||||
<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">
|
||||
function testSteps()
|
||||
{
|
||||
let request = moz_indexedDB.open(window.location.pathname);
|
||||
request.onerror = errorHandler;
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
let event = yield;
|
||||
|
||||
let db = event.result;
|
||||
db.onerror = errorHandler;
|
||||
|
||||
db.setVersion("1").onsuccess = grabEventAndContinueHandler;
|
||||
let event = yield;
|
||||
|
||||
event.transaction.oncomplete = continueToNextStep;
|
||||
|
||||
db.createObjectStore("foo", "", true);
|
||||
yield;
|
||||
|
||||
let transaction = db.transaction("foo");
|
||||
continueToNextStep();
|
||||
yield;
|
||||
|
||||
try {
|
||||
transaction.objectStore("foo");
|
||||
ok(false, "Should have thrown!");
|
||||
}
|
||||
catch (e) {
|
||||
ok(e instanceof IDBDatabaseException, "Got database exception.");
|
||||
is(e.code, IDBDatabaseException.NOT_ALLOWED_ERR, "Good error code.");
|
||||
}
|
||||
|
||||
finishTest();
|
||||
yield;
|
||||
}
|
||||
</script>
|
||||
<script type="text/javascript;version=1.7" src="helpers.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="runTest();"></body>
|
||||
|
||||
</html>
|
79
dom/indexedDB/test/test_transaction_lifetimes_nested.html
Normal file
79
dom/indexedDB/test/test_transaction_lifetimes_nested.html
Normal file
@ -0,0 +1,79 @@
|
||||
<!--
|
||||
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="/MochiKit/packed.js"></script>
|
||||
<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">
|
||||
function testSteps()
|
||||
{
|
||||
let request = moz_indexedDB.open(window.location.pathname);
|
||||
request.onerror = errorHandler;
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
let event = yield;
|
||||
|
||||
let db = event.result;
|
||||
db.onerror = errorHandler;
|
||||
|
||||
db.setVersion("1").onsuccess = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
event.transaction.oncomplete = continueToNextStep;
|
||||
db.createObjectStore("foo", "");
|
||||
yield;
|
||||
|
||||
let transaction1 = db.transaction("foo");
|
||||
is(transaction1.readyState, IDBTransaction.INITIAL, "Correct readyState");
|
||||
|
||||
let transaction2;
|
||||
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
let thread = Components.classes["@mozilla.org/thread-manager;1"]
|
||||
.getService()
|
||||
.currentThread;
|
||||
|
||||
let eventHasRun;
|
||||
|
||||
thread.dispatch(function() {
|
||||
eventHasRun = true;
|
||||
|
||||
is(transaction1.readyState, IDBTransaction.INITIAL,
|
||||
"Correct readyState");
|
||||
|
||||
transaction2 = db.transaction("foo");
|
||||
is(transaction2.readyState, IDBTransaction.INITIAL,
|
||||
"Correct readyState");
|
||||
|
||||
}, Components.interfaces.nsIThread.DISPATCH_NORMAL);
|
||||
|
||||
while (!eventHasRun) {
|
||||
thread.processNextEvent(false);
|
||||
}
|
||||
|
||||
is(transaction1.readyState, IDBTransaction.INITIAL, "Correct readyState");
|
||||
|
||||
ok(transaction2, "Non-null transaction2");
|
||||
is(transaction2.readyState, IDBTransaction.DONE, "Correct readyState");
|
||||
|
||||
continueToNextStep();
|
||||
yield;
|
||||
|
||||
is(transaction1.readyState, IDBTransaction.DONE, "Correct readyState");
|
||||
|
||||
finishTest();
|
||||
yield;
|
||||
}
|
||||
</script>
|
||||
<script type="text/javascript;version=1.7" src="helpers.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="runTest();"></body>
|
||||
|
||||
</html>
|
@ -162,3 +162,16 @@ interface nsIThreadEventFilter : nsISupports
|
||||
*/
|
||||
[notxpcom] boolean acceptEvent(in nsIRunnable event);
|
||||
};
|
||||
|
||||
/**
|
||||
* Temporary interface, will be merged into nsIThreadInternal.
|
||||
*/
|
||||
[scriptable, uuid(718e9346-74cb-4859-8bcc-c9ec37bfb668)]
|
||||
interface nsIThreadInternal2 : nsIThreadInternal
|
||||
{
|
||||
/**
|
||||
* The current recursion depth, 0 when no events are running, 1 when a single
|
||||
* event is running, and higher when nested events are running.
|
||||
*/
|
||||
readonly attribute unsigned long recursionDepth;
|
||||
};
|
||||
|
@ -157,6 +157,7 @@ NS_IMPL_THREADSAFE_RELEASE(nsThread)
|
||||
NS_INTERFACE_MAP_BEGIN(nsThread)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIThread)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIThreadInternal)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIThreadInternal2)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIEventTarget)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIThread)
|
||||
@ -737,6 +738,17 @@ nsThread::nsChainedEventQueue::PutEvent(nsIRunnable *event)
|
||||
return val;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsIThreadInternal2
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsThread::GetRecursionDepth(PRUint32 *depth)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(depth);
|
||||
*depth = mRunningEvent;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -48,13 +48,14 @@
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
// A native thread
|
||||
class nsThread : public nsIThreadInternal, public nsISupportsPriority
|
||||
class nsThread : public nsIThreadInternal2, public nsISupportsPriority
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIEVENTTARGET
|
||||
NS_DECL_NSITHREAD
|
||||
NS_DECL_NSITHREADINTERNAL
|
||||
NS_DECL_NSITHREADINTERNAL2
|
||||
NS_DECL_NSISUPPORTSPRIORITY
|
||||
|
||||
nsThread();
|
||||
|
Loading…
Reference in New Issue
Block a user