mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Hook up threading, storage connection stuff.
This commit is contained in:
parent
8962ae39bc
commit
2cfe2e3512
192
dom/indexedDB/AsyncConnectionHelper.cpp
Normal file
192
dom/indexedDB/AsyncConnectionHelper.cpp
Normal file
@ -0,0 +1,192 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Indexed Database.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* The Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Ben Turner <bent.mozilla@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "AsyncConnectionHelper.h"
|
||||
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#include "IDBEvents.h"
|
||||
|
||||
USING_INDEXEDDB_NAMESPACE
|
||||
|
||||
AsyncConnectionHelper::AsyncConnectionHelper(
|
||||
nsCOMPtr<mozIStorageConnection>& aConnection,
|
||||
nsIDOMEventTarget* aTarget)
|
||||
: mConnection(aConnection),
|
||||
mTarget(aTarget),
|
||||
mErrorCode(0),
|
||||
mError(PR_FALSE)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ASSERTION(mTarget, "Null target!");
|
||||
}
|
||||
|
||||
AsyncConnectionHelper::~AsyncConnectionHelper()
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
NS_ASSERTION(mErrorCode == NOREPLY,
|
||||
"This should only happen if NOREPLY was returned!");
|
||||
|
||||
nsIDOMEventTarget* target;
|
||||
mTarget.forget(&target);
|
||||
|
||||
nsCOMPtr<nsIThread> mainThread;
|
||||
NS_GetMainThread(getter_AddRefs(mainThread));
|
||||
|
||||
if (mainThread) {
|
||||
NS_ProxyRelease(mainThread, target);
|
||||
}
|
||||
else {
|
||||
NS_WARNING("Couldn't get the main thread?! Leaking instead of crashing.");
|
||||
}
|
||||
}
|
||||
|
||||
NS_ASSERTION(!mTarget, "Should have been released before now!");
|
||||
NS_ASSERTION(!mDatabaseThread, "Should have been released before now!");
|
||||
}
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS1(AsyncConnectionHelper, nsIRunnable)
|
||||
|
||||
NS_IMETHODIMP
|
||||
AsyncConnectionHelper::Run()
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
if (mError) {
|
||||
OnError(mTarget, mErrorCode);
|
||||
}
|
||||
else {
|
||||
OnSuccess(mTarget);
|
||||
}
|
||||
|
||||
mTarget = nsnull;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
{
|
||||
PRBool ok;
|
||||
NS_ASSERTION(NS_SUCCEEDED(mDatabaseThread->IsOnCurrentThread(&ok)) && ok,
|
||||
"Wrong thread!");
|
||||
mDatabaseThread = nsnull;
|
||||
}
|
||||
#endif
|
||||
|
||||
mErrorCode = DoDatabaseWork();
|
||||
|
||||
if (mErrorCode != NOREPLY) {
|
||||
mError = mErrorCode != OK;
|
||||
|
||||
return NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
AsyncConnectionHelper::Dispatch(nsIThread* aDatabaseThread)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
{
|
||||
PRBool sameThread;
|
||||
nsresult rv = aDatabaseThread->IsOnCurrentThread(&sameThread);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "IsOnCurrentThread failed!");
|
||||
NS_ASSERTION(!sameThread, "Dispatching to main thread not supported!");
|
||||
}
|
||||
mDatabaseThread = aDatabaseThread;
|
||||
#endif
|
||||
|
||||
return aDatabaseThread->Dispatch(this, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
void
|
||||
AsyncConnectionHelper::OnSuccess(nsIDOMEventTarget* aTarget)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
nsCOMPtr<nsIWritableVariant> variant =
|
||||
do_CreateInstance(NS_VARIANT_CONTRACTID);
|
||||
if (!variant) {
|
||||
NS_ERROR("Couldn't create variant!");
|
||||
return;
|
||||
}
|
||||
|
||||
GetSuccessResult(variant);
|
||||
|
||||
if (NS_FAILED(variant->SetWritable(PR_FALSE))) {
|
||||
NS_ERROR("Failed to make variant readonly!");
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMEvent> event(IDBSuccessEvent::Create(variant));
|
||||
if (!event) {
|
||||
NS_ERROR("Failed to create event!");
|
||||
return;
|
||||
}
|
||||
|
||||
PRBool dummy;
|
||||
aTarget->DispatchEvent(event, &dummy);
|
||||
}
|
||||
|
||||
void
|
||||
AsyncConnectionHelper::OnError(nsIDOMEventTarget* aTarget,
|
||||
PRUint16 aErrorCode)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
// Make an error event and fire it at the target.
|
||||
nsCOMPtr<nsIDOMEvent> event(IDBErrorEvent::Create(aErrorCode));
|
||||
if (!event) {
|
||||
NS_ERROR("Failed to create event!");
|
||||
return;
|
||||
}
|
||||
|
||||
PRBool dummy;
|
||||
aTarget->DispatchEvent(event, &dummy);
|
||||
}
|
||||
|
||||
void
|
||||
AsyncConnectionHelper::GetSuccessResult(nsIWritableVariant* /* aResult */)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
// Leave the variant set to empty.
|
||||
}
|
139
dom/indexedDB/AsyncConnectionHelper.h
Normal file
139
dom/indexedDB/AsyncConnectionHelper.h
Normal file
@ -0,0 +1,139 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Indexed Database.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* The Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Ben Turner <bent.mozilla@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef mozilla_dom_indexeddb_asyncconnectionhelper_h__
|
||||
#define mozilla_dom_indexeddb_asyncconnectionhelper_h__
|
||||
|
||||
// Only meant to be included in IndexedDB source files, not exported.
|
||||
#include "IndexedDatabase.h"
|
||||
|
||||
#include "mozIStorageConnection.h"
|
||||
#include "nsIDOMEventTarget.h"
|
||||
#include "nsIRunnable.h"
|
||||
#include "nsIThread.h"
|
||||
#include "nsIVariant.h"
|
||||
|
||||
BEGIN_INDEXEDDB_NAMESPACE
|
||||
|
||||
/**
|
||||
* Must be subclassed. The subclass must implement DoDatabaseWork. It may then
|
||||
* choose to implement OnSuccess and OnError depending on the needs of the
|
||||
* subclass. If the default implementation of OnSuccess is desired then the
|
||||
* subclass can implement GetSuccessResult to properly set the result of the
|
||||
* success event. Call Dispatch to start the database operation. Must be created
|
||||
* and Dispatched from the main thread only. Target thread may not be the main
|
||||
* thread.
|
||||
*/
|
||||
class AsyncConnectionHelper : public nsIRunnable
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
/**
|
||||
* Return this code from DoDatabaseWork to signal the main thread that the
|
||||
* database operation suceeded. Once the main thread receives this
|
||||
* notification the OnSuccess callback will be invoked allowing the subclass
|
||||
* to fire a success event or otherwise note the completed operation.
|
||||
*/
|
||||
static const PRUint16 OK = PR_UINT16_MAX;
|
||||
|
||||
/**
|
||||
* Return this code from DoDatabaseWork to prevent the helper from firing a
|
||||
* response to the main thread. Note that you must hold an additional
|
||||
* reference to the helper somewhere if further processing is needed or else
|
||||
* the helper will be deleted after returning from DoDatabaseWork.
|
||||
*/
|
||||
static const PRUint16 NOREPLY = OK - 1;
|
||||
|
||||
nsresult Dispatch(nsIThread* aDatabaseThread);
|
||||
|
||||
protected:
|
||||
AsyncConnectionHelper(nsCOMPtr<mozIStorageConnection>& aConnection,
|
||||
nsIDOMEventTarget* aTarget);
|
||||
|
||||
virtual ~AsyncConnectionHelper();
|
||||
|
||||
/**
|
||||
* This callback is run on the database thread. It should return a valid error
|
||||
* code from nsIIDBDatabaseError or one of the two special values above.
|
||||
*/
|
||||
virtual PRUint16 DoDatabaseWork() = 0;
|
||||
|
||||
/**
|
||||
* This callback is run on the main thread if the DoDatabaseWork returned OK.
|
||||
* The default implementation constructs an IDBSuccessEvent with its result
|
||||
* property set to the value returned by GetSuccessResult. The event is then
|
||||
* fired at the target.
|
||||
*/
|
||||
virtual void OnSuccess(nsIDOMEventTarget* aTarget);
|
||||
|
||||
/**
|
||||
* This callback is run on the main thread if DoDatabaseWork returned an error
|
||||
* code. The default implementation constructs an IDBErrorEvent for the error
|
||||
* code and fires it at the target.
|
||||
*/
|
||||
virtual void OnError(nsIDOMEventTarget* aTarget,
|
||||
PRUint16 aErrorCode);
|
||||
|
||||
/**
|
||||
* This function is called from the default implementation of OnSuccess. A
|
||||
* valid nsIWritableVariant is passed into the function which can be modified
|
||||
* by the subclass. The default implementation returns an nsIVariant that is
|
||||
* set to nsIDataType::VTYPE_EMPTY.
|
||||
*/
|
||||
virtual void GetSuccessResult(nsIWritableVariant* aVariant);
|
||||
|
||||
protected:
|
||||
nsCOMPtr<mozIStorageConnection>& mConnection;
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIDOMEventTarget> mTarget;
|
||||
|
||||
#ifdef DEBUG
|
||||
nsCOMPtr<nsIThread> mDatabaseThread;
|
||||
#endif
|
||||
|
||||
PRUint16 mErrorCode;
|
||||
PRPackedBool mError;
|
||||
};
|
||||
|
||||
END_INDEXEDDB_NAMESPACE
|
||||
|
||||
#endif // mozilla_dom_indexeddb_asyncconnectionhelper_h__
|
@ -1,527 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Indexed Database.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* The Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Ben Turner <bent.mozilla@gmail.com>
|
||||
* Shawn Wilsher <me@shawnwilsher.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "AsyncDatabaseConnection.h"
|
||||
|
||||
#include "nsIIDBObjectStore.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIVariant.h"
|
||||
|
||||
#include "nsAppDirectoryServiceDefs.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsDirectoryServiceUtils.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsXPCOMCID.h"
|
||||
|
||||
#include "mozilla/Storage.h"
|
||||
|
||||
#include "IDBEvents.h"
|
||||
|
||||
#define DB_SCHEMA_VERSION 1
|
||||
|
||||
USING_INDEXEDDB_NAMESPACE
|
||||
|
||||
namespace {
|
||||
|
||||
PRUint32 gConnectionCount = 0;
|
||||
nsIThread* gDatabaseThread = nsnull;
|
||||
PRBool gXPCOMShutdown = PR_FALSE;
|
||||
|
||||
struct ObjectStore
|
||||
{
|
||||
ObjectStore() : autoIncrement(PR_FALSE), readerCount(0), writing(PR_FALSE) { }
|
||||
|
||||
nsString name;
|
||||
nsString keyPath;
|
||||
PRBool autoIncrement;
|
||||
|
||||
nsTArray<nsString> mIndexes;
|
||||
PRUint32 readerCount;
|
||||
PRBool writing;
|
||||
};
|
||||
|
||||
nsTArray<ObjectStore>* gObjectStores = nsnull;
|
||||
|
||||
class ShutdownObserver : public nsIObserver
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
NS_IMETHOD
|
||||
Observe(nsISupports* aSubject,
|
||||
const char* aTopic,
|
||||
const PRUnichar* aData)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ASSERTION(!gXPCOMShutdown, "Already shutdown?!");
|
||||
|
||||
if (gDatabaseThread) {
|
||||
nsresult rv = gDatabaseThread->Shutdown();
|
||||
NS_RELEASE(gDatabaseThread);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
delete gObjectStores;
|
||||
gObjectStores = nsnull;
|
||||
|
||||
gXPCOMShutdown = PR_TRUE;
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
already_AddRefed<nsIDOMEvent>
|
||||
MakeSuccessEventBool(PRBool aResult)
|
||||
{
|
||||
nsCOMPtr<nsIWritableVariant> variant =
|
||||
do_CreateInstance(NS_VARIANT_CONTRACTID);
|
||||
NS_ENSURE_TRUE(variant, nsnull);
|
||||
|
||||
nsresult rv = variant->SetAsBool(aResult);
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
|
||||
rv = variant->SetWritable(PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
|
||||
nsCOMPtr<nsIDOMEvent> event(IDBSuccessEvent::Create(variant));
|
||||
return event.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<nsIDOMEvent>
|
||||
MakeSuccessEvent(nsISupports* aResult)
|
||||
{
|
||||
nsCOMPtr<nsIWritableVariant> variant =
|
||||
do_CreateInstance(NS_VARIANT_CONTRACTID);
|
||||
NS_ENSURE_TRUE(variant, nsnull);
|
||||
|
||||
nsresult rv = variant->SetAsISupports(aResult);
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
|
||||
rv = variant->SetWritable(PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
|
||||
nsCOMPtr<nsIDOMEvent> event(IDBSuccessEvent::Create(variant));
|
||||
return event.forget();
|
||||
}
|
||||
|
||||
class DatabaseWorkHelper : public nsRunnable
|
||||
{
|
||||
public:
|
||||
static const PRUint16 OK = PR_UINT16_MAX;
|
||||
|
||||
DatabaseWorkHelper(nsIDOMEventTarget* aTarget)
|
||||
: mTarget(aTarget), mError(PR_FALSE), mErrorCode(0)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
}
|
||||
|
||||
virtual ~DatabaseWorkHelper()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ASSERTION(!mTarget, "Badness!");
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run()
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
if (mError) {
|
||||
// Automatically fire error events.
|
||||
nsCOMPtr<nsIDOMEvent> event(IDBErrorEvent::Create(mErrorCode));
|
||||
PRBool dummy;
|
||||
mTarget->DispatchEvent(event, &dummy);
|
||||
}
|
||||
else {
|
||||
OnSuccess();
|
||||
}
|
||||
|
||||
mTarget = nsnull;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
// Don't let the function mess with mTarget on the wrong thread!
|
||||
{
|
||||
nsCOMPtr<nsIDOMEventTarget> target;
|
||||
target.swap(mTarget);
|
||||
|
||||
mErrorCode = DoDatabaseWork();
|
||||
|
||||
target.swap(mTarget);
|
||||
}
|
||||
|
||||
mError = mErrorCode != OK;
|
||||
|
||||
NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Dispatch()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
if (!gDatabaseThread) {
|
||||
return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
|
||||
}
|
||||
return gDatabaseThread->Dispatch(this, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual PRUint16 DoDatabaseWork() = 0;
|
||||
virtual void OnSuccess() = 0;
|
||||
|
||||
nsCOMPtr<nsIDOMEventTarget> mTarget;
|
||||
|
||||
private:
|
||||
PRBool mError;
|
||||
PRUint16 mErrorCode;
|
||||
};
|
||||
|
||||
class CreateObjectStoreHelper : public DatabaseWorkHelper
|
||||
{
|
||||
public:
|
||||
CreateObjectStoreHelper(const nsAString& aName,
|
||||
const nsAString& aKeyPath,
|
||||
PRBool aAutoIncrement,
|
||||
nsIDOMEventTarget* aTarget)
|
||||
: DatabaseWorkHelper(aTarget), mName(aName), mKeyPath(aKeyPath),
|
||||
mAutoIncrement(aAutoIncrement)
|
||||
{ }
|
||||
|
||||
PRUint16
|
||||
DoDatabaseWork()
|
||||
{
|
||||
PRUint32 count = gObjectStores->Length();
|
||||
for (PRUint32 index = 0; index < count; index++) {
|
||||
if (gObjectStores->ElementAt(index).name.Equals(mName)) {
|
||||
return nsIIDBDatabaseError::CONSTRAINT_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
ObjectStore* newStore = gObjectStores->AppendElement();
|
||||
newStore->name.Assign(mName);
|
||||
newStore->keyPath.Assign(mKeyPath);
|
||||
newStore->autoIncrement = mAutoIncrement;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
void
|
||||
OnSuccess()
|
||||
{
|
||||
nsCOMPtr<nsIDOMEvent> event(MakeSuccessEventBool(PR_TRUE));
|
||||
PRBool dummy;
|
||||
mTarget->DispatchEvent(event, &dummy);
|
||||
}
|
||||
|
||||
protected:
|
||||
nsString mName;
|
||||
nsString mKeyPath;
|
||||
PRBool mAutoIncrement;
|
||||
};
|
||||
|
||||
class OpenObjectStoreHelper : public DatabaseWorkHelper
|
||||
{
|
||||
public:
|
||||
OpenObjectStoreHelper(const nsAString& aName,
|
||||
PRUint16 aMode,
|
||||
nsIDOMEventTarget* aTarget)
|
||||
: DatabaseWorkHelper(aTarget), mName(aName), mMode(aMode)
|
||||
{ }
|
||||
|
||||
PRUint16
|
||||
DoDatabaseWork()
|
||||
{
|
||||
PRUint32 count = gObjectStores->Length();
|
||||
for (PRUint32 index = 0; index < count; index++) {
|
||||
ObjectStore& store = gObjectStores->ElementAt(index);
|
||||
if (store.name.Equals(mName)) {
|
||||
if (mMode == nsIIDBObjectStore::READ_WRITE) {
|
||||
store.writing = PR_TRUE;
|
||||
}
|
||||
store.readerCount++;
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
|
||||
return nsIIDBDatabaseError::NOT_FOUND_ERR;
|
||||
}
|
||||
|
||||
void
|
||||
OnSuccess()
|
||||
{
|
||||
nsCOMPtr<nsIDOMEvent> event(MakeSuccessEventBool(PR_TRUE));
|
||||
PRBool dummy;
|
||||
mTarget->DispatchEvent(event, &dummy);
|
||||
}
|
||||
|
||||
protected:
|
||||
nsString mName;
|
||||
PRUint16 mMode;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates the needed tables and their indexes.
|
||||
*
|
||||
* @param aDBConn
|
||||
* The database connection to create the tables on.
|
||||
*/
|
||||
nsresult
|
||||
CreateTables(mozIStorageConnection* aDBConn)
|
||||
{
|
||||
NS_PRECONDITION(!NS_IsMainThread(),
|
||||
"Creating tables on the main thread!");
|
||||
NS_PRECONDITION(aDBConn, "Passing a null database connection!");
|
||||
|
||||
// Table `database`
|
||||
nsresult rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"CREATE TABLE database ("
|
||||
"name TEXT NOT NULL, "
|
||||
"description TEXT NOT NULL, "
|
||||
"version TEXT DEFAULT NULL, "
|
||||
"UNIQUE (name)"
|
||||
");"
|
||||
));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Table `object_store`
|
||||
rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"CREATE TABLE object_store ("
|
||||
"id INTEGER DEFAULT NULL, "
|
||||
"name TEXT NOT NULL, "
|
||||
"key_path TEXT DEFAULT NULL, "
|
||||
"autoincrement INTEGER NOT NULL DEFAULT 0, "
|
||||
"readers INTEGER NOT NULL DEFAULT 0, "
|
||||
"isWriting INTEGER NOT NULL DEFAULT 0, "
|
||||
"PRIMARY KEY (id), "
|
||||
"UNIQUE (name)"
|
||||
");"
|
||||
));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Table `object_data`
|
||||
rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"CREATE TABLE object_data ("
|
||||
"id INTEGER NOT NULL AUTOINCREMENT, "
|
||||
"object_store_id INTEGER NOT NULL, "
|
||||
"data TEXT NOT NULL, "
|
||||
"key_value TEXT DEFAULT NULL, "
|
||||
"PRIMARY KEY (id), "
|
||||
"FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE CASCADE"
|
||||
");"
|
||||
));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"CREATE INDEX key_index "
|
||||
"ON object_data (id, object_store_id);"
|
||||
));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Table `ai_object_data`
|
||||
rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"CREATE TABLE ai_object_data ("
|
||||
"id INTEGER NOT NULL AUTOINCREMENT, "
|
||||
"object_store_id INTEGER NOT NULL, "
|
||||
"data TEXT NOT NULL, "
|
||||
"PRIMARY KEY (id), "
|
||||
"FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE CASCADE"
|
||||
");"
|
||||
));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"CREATE INDEX key_index "
|
||||
"ON ai_object_data (id, object_store_id);"
|
||||
));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return aDBConn->SetSchemaVersion(DB_SCHEMA_VERSION);
|
||||
}
|
||||
|
||||
already_AddRefed<mozIStorageConnection>
|
||||
NewConnection(const nsCString& aOrigin, bool aDeleteIfExists = false);
|
||||
|
||||
bool
|
||||
ConnectionReady(nsCOMPtr<mozIStorageConnection>& aDBConn,
|
||||
const nsCString& aOrigin)
|
||||
{
|
||||
NS_PRECONDITION(!NS_IsMainThread(),
|
||||
"Checking a database on the main thread!");
|
||||
NS_PRECONDITION(aDBConn, "Passing a null database connection!");
|
||||
|
||||
// Check to make sure that the database schema is correct.
|
||||
PRInt32 schemaVersion;
|
||||
nsresult rv = aDBConn->GetSchemaVersion(&schemaVersion);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
// If the connection is not at the right schema version, nuke it.
|
||||
if (schemaVersion != DB_SCHEMA_VERSION) {
|
||||
aDBConn = NewConnection(aOrigin, true);
|
||||
NS_ENSURE_TRUE(aDBConn, false);
|
||||
rv = CreateTables(aDBConn);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
// Turn on foreign key constraints in debug builds to catch bugs!
|
||||
(void)aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"PRAGMA foreign_keys = ON;"
|
||||
));
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
already_AddRefed<mozIStorageConnection>
|
||||
NewConnection(const nsCString& aOrigin,
|
||||
bool aDeleteIfExists)
|
||||
{
|
||||
NS_PRECONDITION(!NS_IsMainThread(),
|
||||
"Opening a database on the main thread!");
|
||||
|
||||
nsCOMPtr<nsIFile> dbFile;
|
||||
nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
|
||||
getter_AddRefs(dbFile));
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
rv = dbFile->Append(NS_LITERAL_STRING("indexedDB_files"));
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
// Replace illegal filename characters.
|
||||
char illegalChars[] = {':', '/', '\\' };
|
||||
nsCString fname(aOrigin);
|
||||
for (size_t i = 0; i < NS_ARRAY_LENGTH(illegalChars); i++) {
|
||||
fname.ReplaceChar(illegalChars[i], '_');
|
||||
}
|
||||
fname.AppendLiteral(".sqlite");
|
||||
rv = dbFile->Append(NS_ConvertUTF8toUTF16(fname));
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
|
||||
if (aDeleteIfExists) {
|
||||
rv = dbFile->Remove(PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
}
|
||||
|
||||
nsCOMPtr<mozIStorageService> ss =
|
||||
do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
|
||||
|
||||
nsCOMPtr<mozIStorageConnection> conn;
|
||||
rv = ss->OpenDatabase(dbFile, getter_AddRefs(conn));
|
||||
if (rv == NS_ERROR_FILE_CORRUPTED) {
|
||||
// Nuke the database file. The web services can recreate their data.
|
||||
rv = dbFile->Remove(PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
rv = ss->OpenDatabase(dbFile, getter_AddRefs(conn));
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
|
||||
NS_ENSURE_TRUE(ConnectionReady(conn, aOrigin), false);
|
||||
return conn.forget();
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
NS_IMPL_ISUPPORTS1(ShutdownObserver, nsIObserver);
|
||||
|
||||
// static
|
||||
AsyncDatabaseConnection*
|
||||
AsyncDatabaseConnection::OpenConnection(const nsAString& aName,
|
||||
const nsAString& aDescription,
|
||||
PRBool aReadOnly)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
nsAutoPtr<AsyncDatabaseConnection> connection(new AsyncDatabaseConnection());
|
||||
|
||||
if (!gConnectionCount++) {
|
||||
if (gXPCOMShutdown) {
|
||||
NS_ERROR("Creating a new connection after shutdown!");
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs =
|
||||
do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
|
||||
NS_ENSURE_TRUE(obs, nsnull);
|
||||
|
||||
nsCOMPtr<nsIObserver> observer(new ShutdownObserver());
|
||||
nsresult rv = obs->AddObserver(observer, "xpcom-shutdown-threads",
|
||||
PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
|
||||
NS_ASSERTION(!gObjectStores, "Already got a store?!");
|
||||
gObjectStores = new nsAutoTArray<ObjectStore, 10>;
|
||||
|
||||
rv = NS_NewThread(&gDatabaseThread);
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
}
|
||||
|
||||
NS_ASSERTION(gDatabaseThread, "No thread?!");
|
||||
return connection.forget();
|
||||
}
|
||||
|
||||
AsyncDatabaseConnection::AsyncDatabaseConnection()
|
||||
: mReadOnly(PR_FALSE)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
}
|
||||
|
||||
AsyncDatabaseConnection::~AsyncDatabaseConnection()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
}
|
||||
|
||||
nsresult
|
||||
AsyncDatabaseConnection::CreateObjectStore(const nsAString& aName,
|
||||
const nsAString& aKeyPath,
|
||||
PRBool aAutoIncrement,
|
||||
nsIDOMEventTarget* aTarget)
|
||||
{
|
||||
nsRefPtr<CreateObjectStoreHelper> helper =
|
||||
new CreateObjectStoreHelper(aName, aKeyPath, aAutoIncrement, aTarget);
|
||||
return helper->Dispatch();
|
||||
}
|
||||
|
||||
nsresult
|
||||
AsyncDatabaseConnection::OpenObjectStore(const nsAString& aName,
|
||||
PRUint16 aMode,
|
||||
nsIDOMEventTarget* aTarget)
|
||||
{
|
||||
nsRefPtr<OpenObjectStoreHelper> helper =
|
||||
new OpenObjectStoreHelper(aName, aMode, aTarget);
|
||||
return helper->Dispatch();
|
||||
}
|
@ -39,17 +39,27 @@
|
||||
|
||||
#include "IDBDatabaseRequest.h"
|
||||
|
||||
#include "mozilla/Storage.h"
|
||||
#include "nsAppDirectoryServiceDefs.h"
|
||||
#include "nsDirectoryServiceUtils.h"
|
||||
#include "nsDOMClassInfo.h"
|
||||
#include "nsDOMLists.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#include "AsyncConnectionHelper.h"
|
||||
#include "IDBEvents.h"
|
||||
#include "IDBRequest.h"
|
||||
#include "LazyIdleThread.h"
|
||||
|
||||
#define DB_SCHEMA_VERSION 1
|
||||
|
||||
USING_INDEXEDDB_NAMESPACE
|
||||
|
||||
namespace {
|
||||
|
||||
const PRUint32 kDefaultTimeoutMS = 5000;
|
||||
const PRUint32 kDefaultDatabaseTimeoutMS = 5000;
|
||||
const PRUint32 kDefaultThreadTimeoutMS = 30000;
|
||||
|
||||
inline
|
||||
nsISupports*
|
||||
@ -59,6 +69,200 @@ isupports_cast(IDBDatabaseRequest* aClassPtr)
|
||||
static_cast<IDBRequest::Generator*>(aClassPtr));
|
||||
}
|
||||
|
||||
class CreateObjectStoreHelper : public AsyncConnectionHelper
|
||||
{
|
||||
public:
|
||||
CreateObjectStoreHelper(nsCOMPtr<mozIStorageConnection>& aConnection,
|
||||
nsIDOMEventTarget* aTarget,
|
||||
const nsAString& aName,
|
||||
const nsAString& aKeyPath,
|
||||
PRBool aAutoIncrement)
|
||||
: AsyncConnectionHelper(aConnection, aTarget), mName(aName),
|
||||
mKeyPath(aKeyPath), mAutoIncrement(aAutoIncrement)
|
||||
{ }
|
||||
|
||||
PRUint16 DoDatabaseWork();
|
||||
void GetSuccessResult(nsIWritableVariant* aResult);
|
||||
|
||||
protected:
|
||||
nsString mName;
|
||||
nsString mKeyPath;
|
||||
PRBool mAutoIncrement;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates the needed tables and their indexes.
|
||||
*
|
||||
* @param aDBConn
|
||||
* The database connection to create the tables on.
|
||||
*/
|
||||
nsresult
|
||||
CreateTables(mozIStorageConnection* aDBConn)
|
||||
{
|
||||
NS_PRECONDITION(!NS_IsMainThread(),
|
||||
"Creating tables on the main thread!");
|
||||
NS_PRECONDITION(aDBConn, "Passing a null database connection!");
|
||||
|
||||
// Table `database`
|
||||
nsresult rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"CREATE TABLE database ("
|
||||
"name TEXT NOT NULL, "
|
||||
"description TEXT NOT NULL, "
|
||||
"version TEXT DEFAULT NULL, "
|
||||
"UNIQUE (name)"
|
||||
");"
|
||||
));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Table `object_store`
|
||||
rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"CREATE TABLE object_store ("
|
||||
"id INTEGER NOT NULL, "
|
||||
"name TEXT NOT NULL, "
|
||||
"keyPath TEXT DEFAULT NULL, "
|
||||
"auto_increment INTEGER NOT NULL DEFAULT 0, "
|
||||
"readers INTEGER NOT NULL DEFAULT 0, "
|
||||
"is_writing INTEGER NOT NULL DEFAULT 0, "
|
||||
"PRIMARY KEY (id), "
|
||||
"UNIQUE (name)"
|
||||
");"
|
||||
));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Table `object_data`
|
||||
rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"CREATE TABLE object_data ("
|
||||
"id INTEGER NOT NULL, "
|
||||
"object_store_id INTEGER NOT NULL, "
|
||||
"data TEXT NOT NULL, "
|
||||
"key_value TEXT DEFAULT NULL, "
|
||||
"PRIMARY KEY (id), "
|
||||
"FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE CASCADE"
|
||||
");"
|
||||
));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"CREATE INDEX key_index "
|
||||
"ON object_data (id, object_store_id);"
|
||||
));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Table `ai_object_data`
|
||||
rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"CREATE TABLE ai_object_data ("
|
||||
"id INTEGER NOT NULL, "
|
||||
"object_store_id INTEGER NOT NULL, "
|
||||
"data TEXT NOT NULL, "
|
||||
"PRIMARY KEY (id), "
|
||||
"FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE CASCADE"
|
||||
");"
|
||||
));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"CREATE INDEX ai_key_index "
|
||||
"ON ai_object_data (id, object_store_id);"
|
||||
));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return aDBConn->SetSchemaVersion(DB_SCHEMA_VERSION);
|
||||
}
|
||||
|
||||
already_AddRefed<mozIStorageConnection>
|
||||
NewConnection(const nsCString& aOrigin)
|
||||
{
|
||||
NS_PRECONDITION(!NS_IsMainThread(),
|
||||
"Opening a database on the main thread!");
|
||||
|
||||
nsCOMPtr<nsIFile> dbFile;
|
||||
nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
|
||||
getter_AddRefs(dbFile));
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
|
||||
rv = dbFile->Append(NS_LITERAL_STRING("indexedDB_files"));
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
|
||||
PRBool exists;
|
||||
rv = dbFile->Exists(&exists);
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
|
||||
if (exists) {
|
||||
PRBool isDirectory;
|
||||
rv = dbFile->IsDirectory(&isDirectory);
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
NS_ENSURE_TRUE(isDirectory, nsnull);
|
||||
}
|
||||
else {
|
||||
rv = dbFile->Create(nsIFile::DIRECTORY_TYPE, 0755);
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
}
|
||||
|
||||
// Replace illegal filename characters.
|
||||
static const char illegalChars[] = {':', '/', '\\' };
|
||||
nsCString fname(aOrigin);
|
||||
for (size_t i = 0; i < NS_ARRAY_LENGTH(illegalChars); i++) {
|
||||
fname.ReplaceChar(illegalChars[i], '_');
|
||||
}
|
||||
|
||||
fname.AppendLiteral(".sqlite");
|
||||
|
||||
rv = dbFile->Append(NS_ConvertUTF8toUTF16(fname));
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
|
||||
rv = dbFile->Exists(&exists);
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
|
||||
nsCOMPtr<mozIStorageService> ss =
|
||||
do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
|
||||
|
||||
nsCOMPtr<mozIStorageConnection> conn;
|
||||
rv = ss->OpenDatabase(dbFile, getter_AddRefs(conn));
|
||||
if (rv == NS_ERROR_FILE_CORRUPTED) {
|
||||
// Nuke the database file. The web services can recreate their data.
|
||||
rv = dbFile->Remove(PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
|
||||
exists = PR_FALSE;
|
||||
|
||||
rv = ss->OpenDatabase(dbFile, getter_AddRefs(conn));
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
|
||||
// Check to make sure that the database schema is correct.
|
||||
PRInt32 schemaVersion;
|
||||
rv = conn->GetSchemaVersion(&schemaVersion);
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
|
||||
if (schemaVersion != DB_SCHEMA_VERSION) {
|
||||
if (exists) {
|
||||
// If the connection is not at the right schema version, nuke it.
|
||||
rv = dbFile->Remove(PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
|
||||
rv = ss->OpenDatabase(dbFile, getter_AddRefs(conn));
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
}
|
||||
|
||||
rv = CreateTables(conn);
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
// Check to make sure that the database schema is correct.
|
||||
NS_ASSERTION(NS_SUCCEEDED(conn->GetSchemaVersion(&schemaVersion)) &&
|
||||
schemaVersion == DB_SCHEMA_VERSION,
|
||||
"CreateTables failed!");
|
||||
|
||||
// Turn on foreign key constraints in debug builds to catch bugs!
|
||||
(void)conn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"PRAGMA foreign_keys = ON;"
|
||||
));
|
||||
#endif
|
||||
|
||||
return conn.forget();
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
// static
|
||||
@ -68,9 +272,15 @@ IDBDatabaseRequest::Create(const nsAString& aName,
|
||||
PRBool aReadOnly)
|
||||
{
|
||||
nsRefPtr<IDBDatabaseRequest> db(new IDBDatabaseRequest());
|
||||
db->mDatabase = AsyncDatabaseConnection::OpenConnection(aName, aDescription,
|
||||
aReadOnly);
|
||||
NS_ENSURE_TRUE(db->mDatabase, nsnull);
|
||||
|
||||
db->mStorageThread = new LazyIdleThread(kDefaultThreadTimeoutMS);
|
||||
|
||||
db->mName.Assign(aName);
|
||||
db->mDescription.Assign(aDescription);
|
||||
db->mReadOnly = aReadOnly;
|
||||
|
||||
db->mObjectStores = new nsDOMStringList();
|
||||
db->mIndexes = new nsDOMStringList();
|
||||
|
||||
nsIIDBDatabaseRequest* result;
|
||||
db.forget(&result);
|
||||
@ -84,7 +294,9 @@ IDBDatabaseRequest::IDBDatabaseRequest()
|
||||
|
||||
IDBDatabaseRequest::~IDBDatabaseRequest()
|
||||
{
|
||||
|
||||
if (mStorageThread) {
|
||||
mStorageThread->Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_ADDREF(IDBDatabaseRequest)
|
||||
@ -102,43 +314,46 @@ DOMCI_DATA(IDBDatabaseRequest, IDBDatabaseRequest)
|
||||
NS_IMETHODIMP
|
||||
IDBDatabaseRequest::GetName(nsAString& aName)
|
||||
{
|
||||
NS_NOTYETIMPLEMENTED("Implement me!");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
aName.Assign(mName);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
IDBDatabaseRequest::GetDescription(nsAString& aDescription)
|
||||
{
|
||||
NS_NOTYETIMPLEMENTED("Implement me!");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
aDescription.Assign(mDescription);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
IDBDatabaseRequest::GetVersion(nsAString& aVersion)
|
||||
{
|
||||
NS_NOTYETIMPLEMENTED("Implement me!");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
aVersion.Assign(mVersion);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
IDBDatabaseRequest::GetObjectStores(nsIDOMDOMStringList** aObjectStores)
|
||||
{
|
||||
NS_NOTYETIMPLEMENTED("Implement me!");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
nsCOMPtr<nsIDOMDOMStringList> objectStores(mObjectStores);
|
||||
objectStores.forget(aObjectStores);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
IDBDatabaseRequest::GetIndexes(nsIDOMDOMStringList** aIndexes)
|
||||
{
|
||||
NS_NOTYETIMPLEMENTED("Implement me!");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
nsCOMPtr<nsIDOMDOMStringList> indexes(mIndexes);
|
||||
indexes.forget(aIndexes);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
IDBDatabaseRequest::GetCurrentTransaction(nsIIDBTransaction** aTransaction)
|
||||
{
|
||||
NS_NOTYETIMPLEMENTED("Implement me!");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
nsCOMPtr<nsIIDBTransaction> transaction(mCurrentTransaction);
|
||||
transaction.forget(aTransaction);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -148,8 +363,12 @@ IDBDatabaseRequest::CreateObjectStore(const nsAString& aName,
|
||||
nsIIDBRequest** _retval)
|
||||
{
|
||||
nsRefPtr<IDBRequest> request = GenerateRequest();
|
||||
nsresult rv = mDatabase->CreateObjectStore(aName, aKeyPath, aAutoIncrement,
|
||||
request);
|
||||
NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
|
||||
|
||||
nsRefPtr<CreateObjectStoreHelper> helper =
|
||||
new CreateObjectStoreHelper(mStorage, request, aName, aKeyPath,
|
||||
aAutoIncrement);
|
||||
nsresult rv = helper->Dispatch(mStorageThread);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
IDBRequest* retval;
|
||||
@ -164,9 +383,10 @@ IDBDatabaseRequest::OpenObjectStore(const nsAString& aName,
|
||||
nsIIDBRequest** _retval)
|
||||
{
|
||||
nsRefPtr<IDBRequest> request = GenerateRequest();
|
||||
/*
|
||||
nsresult rv = mDatabase->OpenObjectStore(aName, aMode, request);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
*/
|
||||
IDBRequest* retval;
|
||||
request.forget(&retval);
|
||||
*_retval = retval;
|
||||
@ -245,7 +465,7 @@ IDBDatabaseRequest::OpenTransaction(nsIDOMDOMStringList* aStoreNames,
|
||||
NS_NOTYETIMPLEMENTED("Implement me!");
|
||||
|
||||
if (aArgCount < 2) {
|
||||
aTimeout = kDefaultTimeoutMS;
|
||||
aTimeout = kDefaultDatabaseTimeoutMS;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIIDBRequest> request(GenerateRequest());
|
||||
@ -253,3 +473,16 @@ IDBDatabaseRequest::OpenTransaction(nsIDOMDOMStringList* aStoreNames,
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRUint16
|
||||
CreateObjectStoreHelper::DoDatabaseWork()
|
||||
{
|
||||
mConnection = NewConnection(NS_LITERAL_CSTRING("http://foo.com/bar.html"));
|
||||
return mConnection ? OK : nsIIDBDatabaseError::UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
void
|
||||
CreateObjectStoreHelper::GetSuccessResult(nsIWritableVariant* aResult)
|
||||
{
|
||||
aResult->SetAsBool(PR_TRUE);
|
||||
}
|
||||
|
@ -41,9 +41,12 @@
|
||||
#define mozilla_dom_indexeddb_idbdatabaserequest_h__
|
||||
|
||||
#include "mozilla/dom/indexedDB/IDBRequest.h"
|
||||
#include "mozilla/dom/indexedDB/AsyncDatabaseConnection.h"
|
||||
#include "mozilla/dom/indexedDB/LazyIdleThread.h"
|
||||
|
||||
#include "mozIStorageConnection.h"
|
||||
#include "nsIDOMDOMStringList.h"
|
||||
#include "nsIIDBDatabaseRequest.h"
|
||||
#include "nsIIDBTransaction.h"
|
||||
|
||||
BEGIN_INDEXEDDB_NAMESPACE
|
||||
|
||||
@ -65,7 +68,18 @@ protected:
|
||||
~IDBDatabaseRequest();
|
||||
|
||||
private:
|
||||
nsAutoPtr<AsyncDatabaseConnection> mDatabase;
|
||||
nsString mName;
|
||||
nsString mDescription;
|
||||
PRBool mReadOnly;
|
||||
nsString mVersion;
|
||||
nsCOMPtr<nsIDOMDOMStringList> mObjectStores;
|
||||
nsCOMPtr<nsIDOMDOMStringList> mIndexes;
|
||||
nsCOMPtr<nsIIDBTransaction> mCurrentTransaction;
|
||||
|
||||
nsRefPtr<LazyIdleThread> mStorageThread;
|
||||
|
||||
// Only touched on mStorageThread!
|
||||
nsCOMPtr<mozIStorageConnection> mStorage;
|
||||
};
|
||||
|
||||
END_INDEXEDDB_NAMESPACE
|
||||
|
@ -37,47 +37,37 @@
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef mozilla_dom_indexeddb_asyncdatabaseconnection_h__
|
||||
#define mozilla_dom_indexeddb_asyncdatabaseconnection_h__
|
||||
#ifndef mozilla_dom_indexeddb_idbdatabaserequest_h__
|
||||
#define mozilla_dom_indexeddb_idbdatabaserequest_h__
|
||||
|
||||
#include "mozilla/dom/indexedDB/IndexedDatabase.h"
|
||||
|
||||
class nsIDOMEventTarget;
|
||||
#include "nsIIDBObjectStoreRequest.h"
|
||||
|
||||
BEGIN_INDEXEDDB_NAMESPACE
|
||||
|
||||
/**
|
||||
* This is mostly a fake backend until we have a real one
|
||||
*/
|
||||
|
||||
class AsyncDatabaseConnection
|
||||
class IDBObjectStoreRequest : public IDBRequest::Generator,
|
||||
public nsIIDBObjectStoreRequest
|
||||
{
|
||||
public:
|
||||
static AsyncDatabaseConnection*
|
||||
OpenConnection(const nsAString& aName,
|
||||
const nsAString& aDescription,
|
||||
PRBool aReadOnly);
|
||||
~AsyncDatabaseConnection();
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIIDBOBJECTSTORE
|
||||
NS_DECL_NSIIDBOBJECTSTOREREQUEST
|
||||
|
||||
nsresult
|
||||
CreateObjectStore(const nsAString& aName,
|
||||
static already_AddRefed<nsIIDBObjectStoreRequest>
|
||||
Create(const nsAString& aName,
|
||||
const nsAString& aKeyPath,
|
||||
PRBool aAutoIncrement,
|
||||
nsIDOMEventTarget* aTarget);
|
||||
PRUint16 aMode);
|
||||
|
||||
nsresult
|
||||
OpenObjectStore(const nsAString& aName,
|
||||
PRUint16 aMode,
|
||||
nsIDOMEventTarget* aTarget);
|
||||
protected:
|
||||
IDBObjectStoreRequest();
|
||||
~IDBObjectStoreRequest();
|
||||
|
||||
private:
|
||||
AsyncDatabaseConnection();
|
||||
|
||||
nsString mName;
|
||||
nsString mDescription;
|
||||
PRBool mReadOnly;
|
||||
nsAutoPtr<AsyncDatabaseConnection> mDatabase;
|
||||
};
|
||||
|
||||
END_INDEXEDDB_NAMESPACE
|
||||
|
||||
#endif // mozilla_dom_indexeddb_asyncdatabaseconnection_h__
|
||||
#endif // mozilla_dom_indexeddb_idbdatabaserequest_h__
|
@ -76,16 +76,43 @@ LazyIdleThread::LazyIdleThread(PRUint32 aIdleTimeoutMS)
|
||||
mOwningThread(NS_GetCurrentThread()),
|
||||
mIdleTimeoutMS(aIdleTimeoutMS),
|
||||
mShutdown(PR_FALSE),
|
||||
mThreadHasTimedOut(PR_FALSE)
|
||||
mThreadHasTimedOut(PR_FALSE),
|
||||
mTimeoutDisabledCount(0)
|
||||
{
|
||||
NS_ASSERTION(mOwningThread, "This should never fail!");
|
||||
}
|
||||
|
||||
LazyIdleThread::~LazyIdleThread()
|
||||
{
|
||||
if (!mShutdown) {
|
||||
NS_ASSERTION(NS_GetCurrentThread() == mOwningThread, "Wrong thread!");
|
||||
|
||||
Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LazyIdleThread::EnableIdleTimeout(PRBool aEnable)
|
||||
{
|
||||
if (aEnable) {
|
||||
MutexAutoLock lock(mMutex);
|
||||
if (mTimeoutDisabledCount) {
|
||||
mTimeoutDisabledCount--;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsITimer> timer;
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
NS_ASSERTION(mTimeoutDisabledCount < PR_UINT32_MAX, "Too many calls!");
|
||||
if (mTimeoutDisabledCount < PR_UINT32_MAX) {
|
||||
mTimeoutDisabledCount++;
|
||||
}
|
||||
timer.swap(mIdleTimer);
|
||||
}
|
||||
if (timer) {
|
||||
CancelTimer(timer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -177,35 +204,108 @@ LazyIdleThread::ShutdownThread()
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS3(LazyIdleThread, nsITimerCallback,
|
||||
/**
|
||||
* Cancel any pending timer. May be called from any thread.
|
||||
*/
|
||||
void
|
||||
LazyIdleThread::CancelTimer(nsITimer* aTimer)
|
||||
{
|
||||
NS_ASSERTION(aTimer, "Null timer!");
|
||||
if (NS_FAILED(mOwningThread->Dispatch(new CancelTimerRunnable(aTimer),
|
||||
NS_DISPATCH_NORMAL))) {
|
||||
NS_WARNING("Failed to dispatch CancelTimerRunnable!");
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS5(LazyIdleThread, nsIThread,
|
||||
nsIEventTarget,
|
||||
nsITimerCallback,
|
||||
nsIThreadObserver,
|
||||
nsIObserver)
|
||||
|
||||
/**
|
||||
* Dispatch an event to the thread.
|
||||
*/
|
||||
nsresult
|
||||
LazyIdleThread::Dispatch(nsIRunnable* aEvent)
|
||||
NS_IMETHODIMP
|
||||
LazyIdleThread::Dispatch(nsIRunnable* aEvent,
|
||||
PRUint32 aFlags)
|
||||
{
|
||||
NS_ASSERTION(NS_GetCurrentThread() == mOwningThread, "Wrong thread!");
|
||||
|
||||
nsresult rv = EnsureThread();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return mThread->Dispatch(aEvent, NS_DISPATCH_NORMAL);
|
||||
return mThread->Dispatch(aEvent, aFlags);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
LazyIdleThread::IsOnCurrentThread(PRBool* aIsOnCurrentThread)
|
||||
{
|
||||
if (mThread) {
|
||||
return mThread->IsOnCurrentThread(aIsOnCurrentThread);
|
||||
}
|
||||
|
||||
*aIsOnCurrentThread = PR_FALSE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the PRThread for our thread (if it has been created).
|
||||
*/
|
||||
NS_IMETHODIMP
|
||||
LazyIdleThread::GetPRThread(PRThread** aPRThread)
|
||||
{
|
||||
if (mThread) {
|
||||
return mThread->GetPRThread(aPRThread);
|
||||
}
|
||||
|
||||
*aPRThread = nsnull;
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shut down the thread (if it has been created) and prevent future dispatch.
|
||||
*/
|
||||
void
|
||||
NS_IMETHODIMP
|
||||
LazyIdleThread::Shutdown()
|
||||
{
|
||||
if (!mShutdown) {
|
||||
NS_ASSERTION(NS_GetCurrentThread() == mOwningThread, "Wrong thread!");
|
||||
|
||||
mShutdown = PR_TRUE;
|
||||
|
||||
ShutdownThread();
|
||||
|
||||
mShutdown = PR_TRUE;
|
||||
mOwningThread = nsnull;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* See if there are more events to be processed. This is only supposed to be
|
||||
* called from the thread itself.
|
||||
*/
|
||||
NS_IMETHODIMP
|
||||
LazyIdleThread::HasPendingEvents(PRBool* aHasPendingEvents)
|
||||
{
|
||||
nsCOMPtr<nsIThread> thisThread(NS_GetCurrentThread());
|
||||
NS_ASSERTION(thisThread, "This should never be null!");
|
||||
|
||||
return thisThread->HasPendingEvents(aHasPendingEvents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run another event on the thread. Should only be called from the thread
|
||||
* itself.
|
||||
*/
|
||||
NS_IMETHODIMP
|
||||
LazyIdleThread::ProcessNextEvent(PRBool aMayWait,
|
||||
PRBool* aEventWasProcessed)
|
||||
{
|
||||
nsCOMPtr<nsIThread> thisThread(NS_GetCurrentThread());
|
||||
NS_ASSERTION(thisThread, "This should never be null!");
|
||||
|
||||
return thisThread->ProcessNextEvent(aMayWait, aEventWasProcessed);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -247,7 +347,6 @@ NS_IMETHODIMP
|
||||
LazyIdleThread::OnDispatchedEvent(nsIThreadInternal* /*aThread */)
|
||||
{
|
||||
// This can happen on *any* thread...
|
||||
|
||||
nsCOMPtr<nsITimer> timer;
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
@ -260,9 +359,7 @@ LazyIdleThread::OnDispatchedEvent(nsIThreadInternal* /*aThread */)
|
||||
}
|
||||
|
||||
if (timer) {
|
||||
nsresult rv = mOwningThread->Dispatch(new CancelTimerRunnable(timer),
|
||||
NS_DISPATCH_NORMAL);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
CancelTimer(timer);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
@ -296,7 +393,7 @@ LazyIdleThread::AfterProcessNextEvent(nsIThreadInternal* aThread,
|
||||
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
if (mThreadHasTimedOut) {
|
||||
if (mThreadHasTimedOut || mTimeoutDisabledCount) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -323,8 +420,7 @@ LazyIdleThread::Observe(nsISupports* /* aSubject */,
|
||||
const char* /* aTopic */,
|
||||
const PRUnichar* /* aData */)
|
||||
{
|
||||
NS_ASSERTION(NS_GetCurrentThread() == mOwningThread, "Wrong thread!");
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
ShutdownThread();
|
||||
Shutdown();
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -57,12 +57,15 @@ BEGIN_INDEXEDDB_NAMESPACE
|
||||
* is created on the main thread then it will automatically join its thread on
|
||||
* XPCOM shutdown using the Observer Service.
|
||||
*/
|
||||
class LazyIdleThread : public nsITimerCallback,
|
||||
class LazyIdleThread : public nsIThread,
|
||||
public nsITimerCallback,
|
||||
public nsIThreadObserver,
|
||||
public nsIObserver
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIEVENTTARGET
|
||||
NS_DECL_NSITHREAD
|
||||
NS_DECL_NSITIMERCALLBACK
|
||||
NS_DECL_NSITHREADOBSERVER
|
||||
NS_DECL_NSIOBSERVER
|
||||
@ -74,16 +77,9 @@ public:
|
||||
LazyIdleThread(PRUint32 aIdleTimeoutMS);
|
||||
|
||||
/**
|
||||
* Dispatch an event to the thread, creating it if necessary.
|
||||
* Enables or disables the idle timeout.
|
||||
*/
|
||||
nsresult Dispatch(nsIRunnable* aEvent);
|
||||
|
||||
/**
|
||||
* Join the thread and prevent further event dispatch. It is not possible to
|
||||
* reuse the LazyIdleThread after calling this. Shutdown() will also be called
|
||||
* automatically when the reference count drops to 0.
|
||||
*/
|
||||
void Shutdown();
|
||||
void EnableIdleTimeout(PRBool aEnable);
|
||||
|
||||
private:
|
||||
/**
|
||||
@ -106,6 +102,11 @@ private:
|
||||
*/
|
||||
void ShutdownThread();
|
||||
|
||||
/**
|
||||
* Called to cancel any pending timer.
|
||||
*/
|
||||
void CancelTimer(nsITimer* aTimer);
|
||||
|
||||
/**
|
||||
* Protects mIdleTimer and mThreadHasTimedOut.
|
||||
*/
|
||||
@ -145,6 +146,11 @@ private:
|
||||
* when we're in the process of shutting down mThread.
|
||||
*/
|
||||
PRBool mThreadHasTimedOut;
|
||||
|
||||
/**
|
||||
* Protected by mMutex. Whether or not the timeout is enabled.
|
||||
*/
|
||||
PRUint32 mTimeoutDisabledCount;
|
||||
};
|
||||
|
||||
END_INDEXEDDB_NAMESPACE
|
||||
|
@ -52,7 +52,7 @@ FORCE_STATIC_LIB = 1
|
||||
EXPORTS_NAMESPACES = mozilla/dom/indexedDB
|
||||
|
||||
CPPSRCS = \
|
||||
AsyncDatabaseConnection.cpp \
|
||||
AsyncConnectionHelper.cpp \
|
||||
IDBDatabaseError.cpp \
|
||||
IDBDatabaseRequest.cpp \
|
||||
IDBEvents.cpp \
|
||||
@ -62,7 +62,6 @@ CPPSRCS = \
|
||||
$(NULL)
|
||||
|
||||
EXPORTS_mozilla/dom/indexedDB = \
|
||||
AsyncDatabaseConnection.h \
|
||||
IDBDatabaseError.h \
|
||||
IDBDatabaseRequest.h \
|
||||
IDBEvents.h \
|
||||
|
Loading…
Reference in New Issue
Block a user