зеркало из https://github.com/mozilla/pjs.git
Bug 596377 - 'IndexedDB: Move usage and clearing IO off the main thread'. r=sicking.
This commit is contained in:
Родитель
0620be98b5
Коммит
f287763f50
|
@ -125,6 +125,10 @@ function onUnloadPermission()
|
||||||
var os = Components.classes["@mozilla.org/observer-service;1"]
|
var os = Components.classes["@mozilla.org/observer-service;1"]
|
||||||
.getService(Components.interfaces.nsIObserverService);
|
.getService(Components.interfaces.nsIObserverService);
|
||||||
os.removeObserver(permissionObserver, "perm-changed");
|
os.removeObserver(permissionObserver, "perm-changed");
|
||||||
|
|
||||||
|
var dbManager = Components.classes["@mozilla.org/dom/indexeddb/manager;1"]
|
||||||
|
.getService(nsIIndexedDatabaseManager);
|
||||||
|
dbManager.cancelGetUsageForURI(gPermURI, onIndexedDBUsageCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
function initRow(aPartId)
|
function initRow(aPartId)
|
||||||
|
@ -195,27 +199,16 @@ function setRadioState(aPartId, aValue)
|
||||||
|
|
||||||
function initIndexedDBRow()
|
function initIndexedDBRow()
|
||||||
{
|
{
|
||||||
|
var dbManager = Components.classes["@mozilla.org/dom/indexeddb/manager;1"]
|
||||||
|
.getService(nsIIndexedDatabaseManager);
|
||||||
|
dbManager.getUsageForURI(gPermURI, onIndexedDBUsageCallback);
|
||||||
|
|
||||||
var status = document.getElementById("indexedDBStatus");
|
var status = document.getElementById("indexedDBStatus");
|
||||||
var button = document.getElementById("indexedDBClear");
|
var button = document.getElementById("indexedDBClear");
|
||||||
|
|
||||||
var usage = Components.classes["@mozilla.org/dom/indexeddb/manager;1"]
|
status.value = "";
|
||||||
.getService(nsIIndexedDatabaseManager)
|
status.setAttribute("hidden", "true");
|
||||||
.getUsageForURI(gPermURI);
|
button.setAttribute("hidden", "true");
|
||||||
if (usage) {
|
|
||||||
if (!("DownloadUtils" in window)) {
|
|
||||||
Components.utils.import("resource://gre/modules/DownloadUtils.jsm");
|
|
||||||
}
|
|
||||||
status.value =
|
|
||||||
gBundle.getFormattedString("indexedDBUsage",
|
|
||||||
DownloadUtils.convertByteUnits(usage));
|
|
||||||
status.removeAttribute("hidden");
|
|
||||||
button.removeAttribute("hidden");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
status.value = "";
|
|
||||||
status.setAttribute("hidden", "true");
|
|
||||||
button.setAttribute("hidden", "true");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onIndexedDBClear()
|
function onIndexedDBClear()
|
||||||
|
@ -230,3 +223,25 @@ function onIndexedDBClear()
|
||||||
permissionManager.remove(gPermURI.host, "indexedDB-unlimited");
|
permissionManager.remove(gPermURI.host, "indexedDB-unlimited");
|
||||||
initIndexedDBRow();
|
initIndexedDBRow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onIndexedDBUsageCallback(uri, usage)
|
||||||
|
{
|
||||||
|
if (!uri.equals(gPermURI)) {
|
||||||
|
throw new Error("Callback received for bad URI: " + uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (usage) {
|
||||||
|
if (!("DownloadUtils" in window)) {
|
||||||
|
Components.utils.import("resource://gre/modules/DownloadUtils.jsm");
|
||||||
|
}
|
||||||
|
|
||||||
|
var status = document.getElementById("indexedDBStatus");
|
||||||
|
var button = document.getElementById("indexedDBClear");
|
||||||
|
|
||||||
|
status.value =
|
||||||
|
gBundle.getFormattedString("indexedDBUsage",
|
||||||
|
DownloadUtils.convertByteUnits(usage));
|
||||||
|
status.removeAttribute("hidden");
|
||||||
|
button.removeAttribute("hidden");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -250,7 +250,7 @@ IDBDatabase::Create(nsIScriptContext* aScriptContext,
|
||||||
|
|
||||||
db->mConnection.swap(aConnection);
|
db->mConnection.swap(aConnection);
|
||||||
|
|
||||||
IndexedDatabaseManager* mgr = IndexedDatabaseManager::GetInstance();
|
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
|
||||||
NS_ASSERTION(mgr, "This should never be null!");
|
NS_ASSERTION(mgr, "This should never be null!");
|
||||||
|
|
||||||
if (!mgr->RegisterDatabase(db)) {
|
if (!mgr->RegisterDatabase(db)) {
|
||||||
|
@ -276,7 +276,7 @@ IDBDatabase::IDBDatabase()
|
||||||
IDBDatabase::~IDBDatabase()
|
IDBDatabase::~IDBDatabase()
|
||||||
{
|
{
|
||||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||||
IndexedDatabaseManager* mgr = IndexedDatabaseManager::GetInstance();
|
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
|
||||||
if (mgr) {
|
if (mgr) {
|
||||||
mgr->UnregisterDatabase(this);
|
mgr->UnregisterDatabase(this);
|
||||||
}
|
}
|
||||||
|
@ -287,7 +287,7 @@ IDBDatabase::~IDBDatabase()
|
||||||
|
|
||||||
CloseConnection();
|
CloseConnection();
|
||||||
|
|
||||||
if (mDatabaseId) {
|
if (mDatabaseId && !mInvalidated) {
|
||||||
DatabaseInfo* info;
|
DatabaseInfo* info;
|
||||||
if (!DatabaseInfo::Get(mDatabaseId, &info)) {
|
if (!DatabaseInfo::Get(mDatabaseId, &info)) {
|
||||||
NS_ERROR("This should never fail!");
|
NS_ERROR("This should never fail!");
|
||||||
|
@ -398,6 +398,16 @@ IDBDatabase::Invalidate()
|
||||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||||
PR_AtomicSet(&mInvalidated, 1);
|
PR_AtomicSet(&mInvalidated, 1);
|
||||||
CloseConnection();
|
CloseConnection();
|
||||||
|
|
||||||
|
DatabaseInfo* info;
|
||||||
|
if (!DatabaseInfo::Get(mDatabaseId, &info)) {
|
||||||
|
NS_ERROR("This should never fail!");
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_ASSERTION(info->referenceCount, "Bad reference count!");
|
||||||
|
if (--info->referenceCount == 0) {
|
||||||
|
DatabaseInfo::Remove(mDatabaseId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -406,16 +416,6 @@ IDBDatabase::IsInvalidated()
|
||||||
return !!mInvalidated;
|
return !!mInvalidated;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
IDBDatabase::WaitForConnectionReleased()
|
|
||||||
{
|
|
||||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
|
||||||
TransactionThreadPool* threadPool = TransactionThreadPool::Get();
|
|
||||||
if (threadPool) {
|
|
||||||
threadPool->WaitForAllTransactionsToComplete(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_CLASS(IDBDatabase)
|
NS_IMPL_CYCLE_COLLECTION_CLASS(IDBDatabase)
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBDatabase,
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBDatabase,
|
||||||
|
|
|
@ -122,8 +122,6 @@ public:
|
||||||
void Invalidate();
|
void Invalidate();
|
||||||
bool IsInvalidated();
|
bool IsInvalidated();
|
||||||
|
|
||||||
void WaitForConnectionReleased();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
IDBDatabase();
|
IDBDatabase();
|
||||||
~IDBDatabase();
|
~IDBDatabase();
|
||||||
|
|
|
@ -727,9 +727,7 @@ IDBFactory::Open(const nsAString& aName,
|
||||||
nsRefPtr<CheckPermissionsHelper> permissionHelper =
|
nsRefPtr<CheckPermissionsHelper> permissionHelper =
|
||||||
new CheckPermissionsHelper(openHelper, thread, innerWindow, origin);
|
new CheckPermissionsHelper(openHelper, thread, innerWindow, origin);
|
||||||
|
|
||||||
nsRefPtr<IndexedDatabaseManager> mgr =
|
nsRefPtr<IndexedDatabaseManager> mgr = IndexedDatabaseManager::GetOrCreate();
|
||||||
already_AddRefed<IndexedDatabaseManager>(
|
|
||||||
IndexedDatabaseManager::GetOrCreateInstance());
|
|
||||||
NS_ENSURE_TRUE(mgr, NS_ERROR_FAILURE);
|
NS_ENSURE_TRUE(mgr, NS_ERROR_FAILURE);
|
||||||
|
|
||||||
rv = mgr->WaitForClearAndDispatch(origin, permissionHelper);
|
rv = mgr->WaitForClearAndDispatch(origin, permissionHelper);
|
||||||
|
|
|
@ -42,14 +42,26 @@
|
||||||
#include "nsIFile.h"
|
#include "nsIFile.h"
|
||||||
#include "nsIObserverService.h"
|
#include "nsIObserverService.h"
|
||||||
#include "nsISimpleEnumerator.h"
|
#include "nsISimpleEnumerator.h"
|
||||||
|
#include "nsITimer.h"
|
||||||
|
|
||||||
#include "mozilla/Services.h"
|
#include "mozilla/Services.h"
|
||||||
#include "nsContentUtils.h"
|
#include "nsContentUtils.h"
|
||||||
#include "nsThreadUtils.h"
|
#include "nsThreadUtils.h"
|
||||||
#include "nsXPCOM.h"
|
#include "nsXPCOM.h"
|
||||||
|
#include "nsXPCOMPrivate.h"
|
||||||
|
|
||||||
#include "IDBDatabase.h"
|
#include "IDBDatabase.h"
|
||||||
#include "IDBFactory.h"
|
#include "IDBFactory.h"
|
||||||
|
#include "LazyIdleThread.h"
|
||||||
|
#include "TransactionThreadPool.h"
|
||||||
|
|
||||||
|
// The amount of time, in milliseconds, that our IO thread will stay alive
|
||||||
|
// after the last event it processes.
|
||||||
|
#define DEFAULT_THREAD_TIMEOUT_MS 30000
|
||||||
|
|
||||||
|
// The amount of time, in milliseconds, that we will wait for active database
|
||||||
|
// transactions on shutdown before aborting them.
|
||||||
|
#define DEFAULT_SHUTDOWN_TIMER_MS 30000
|
||||||
|
|
||||||
USING_INDEXEDDB_NAMESPACE
|
USING_INDEXEDDB_NAMESPACE
|
||||||
using namespace mozilla::services;
|
using namespace mozilla::services;
|
||||||
|
@ -58,7 +70,7 @@ namespace {
|
||||||
|
|
||||||
bool gShutdown = false;
|
bool gShutdown = false;
|
||||||
|
|
||||||
// Holds a reference!
|
// Does not hold a reference.
|
||||||
IndexedDatabaseManager* gInstance = nsnull;
|
IndexedDatabaseManager* gInstance = nsnull;
|
||||||
|
|
||||||
// Adds all databases in the hash to the given array.
|
// Adds all databases in the hash to the given array.
|
||||||
|
@ -72,8 +84,8 @@ EnumerateToTArray(const nsACString& aKey,
|
||||||
NS_ASSERTION(aValue, "Null pointer!");
|
NS_ASSERTION(aValue, "Null pointer!");
|
||||||
NS_ASSERTION(aUserArg, "Null pointer!");
|
NS_ASSERTION(aUserArg, "Null pointer!");
|
||||||
|
|
||||||
nsTArray<nsRefPtr<IDBDatabase> >* array =
|
nsTArray<IDBDatabase*>* array =
|
||||||
static_cast<nsTArray<nsRefPtr<IDBDatabase> >* >(aUserArg);
|
static_cast<nsTArray<IDBDatabase*>*>(aUserArg);
|
||||||
|
|
||||||
if (!array->AppendElements(*aValue)) {
|
if (!array->AppendElements(*aValue)) {
|
||||||
NS_WARNING("Out of memory!");
|
NS_WARNING("Out of memory!");
|
||||||
|
@ -98,10 +110,9 @@ IndexedDatabaseManager::~IndexedDatabaseManager()
|
||||||
gInstance = nsnull;
|
gInstance = nsnull;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a raw pointer that carries an owning reference! Lame, but the
|
// static
|
||||||
// singleton factory macros force this.
|
already_AddRefed<IndexedDatabaseManager>
|
||||||
IndexedDatabaseManager*
|
IndexedDatabaseManager::GetOrCreate()
|
||||||
IndexedDatabaseManager::GetOrCreateInstance()
|
|
||||||
{
|
{
|
||||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||||
|
|
||||||
|
@ -110,32 +121,62 @@ IndexedDatabaseManager::GetOrCreateInstance()
|
||||||
return nsnull;
|
return nsnull;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!gInstance) {
|
nsRefPtr<IndexedDatabaseManager> instance(gInstance);
|
||||||
nsRefPtr<IndexedDatabaseManager> instance(new IndexedDatabaseManager());
|
|
||||||
|
if (!instance) {
|
||||||
|
instance = new IndexedDatabaseManager();
|
||||||
|
|
||||||
if (!instance->mLiveDatabases.Init()) {
|
if (!instance->mLiveDatabases.Init()) {
|
||||||
NS_WARNING("Out of memory!");
|
NS_WARNING("Out of memory!");
|
||||||
return nsnull;
|
return nsnull;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to know when to release the singleton.
|
// Make a timer here to avoid potential failures later. We don't actually
|
||||||
|
// initialize the timer until shutdown.
|
||||||
|
instance->mShutdownTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
|
||||||
|
NS_ENSURE_TRUE(instance->mShutdownTimer, nsnull);
|
||||||
|
|
||||||
nsCOMPtr<nsIObserverService> obs = GetObserverService();
|
nsCOMPtr<nsIObserverService> obs = GetObserverService();
|
||||||
|
NS_ENSURE_TRUE(obs, nsnull);
|
||||||
|
|
||||||
|
// We need this callback to know when to shut down all our threads.
|
||||||
nsresult rv = obs->AddObserver(instance, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
|
nsresult rv = obs->AddObserver(instance, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
|
||||||
PR_FALSE);
|
PR_FALSE);
|
||||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||||
|
|
||||||
instance.forget(&gInstance);
|
// We don't really need this callback but we want the observer service to
|
||||||
|
// hold us alive until XPCOM shutdown. That way other consumers can continue
|
||||||
|
// to use this service until shutdown.
|
||||||
|
rv = obs->AddObserver(instance, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID,
|
||||||
|
PR_FALSE);
|
||||||
|
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||||
|
|
||||||
|
// Make a lazy thread for any IO we need (like clearing or enumerating the
|
||||||
|
// contents of indexedDB database directories).
|
||||||
|
instance->mIOThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS);
|
||||||
|
|
||||||
|
// The observer service will hold our last reference, don't AddRef here.
|
||||||
|
gInstance = instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IF_ADDREF(gInstance);
|
return instance.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
IndexedDatabaseManager*
|
||||||
|
IndexedDatabaseManager::Get()
|
||||||
|
{
|
||||||
|
// Does not return an owning reference.
|
||||||
return gInstance;
|
return gInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Does not return an owning reference.
|
// static
|
||||||
IndexedDatabaseManager*
|
IndexedDatabaseManager*
|
||||||
IndexedDatabaseManager::GetInstance()
|
IndexedDatabaseManager::FactoryCreate()
|
||||||
{
|
{
|
||||||
return gInstance;
|
// Returns a raw pointer that carries an owning reference! Lame, but the
|
||||||
|
// singleton factory macros force this.
|
||||||
|
return GetOrCreate().get();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called when an IDBDatabase is constructed.
|
// Called when an IDBDatabase is constructed.
|
||||||
|
@ -187,6 +228,38 @@ IndexedDatabaseManager::UnregisterDatabase(IDBDatabase* aDatabase)
|
||||||
NS_ERROR("Didn't know anything about this database!");
|
NS_ERROR("Didn't know anything about this database!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Called when OriginClearRunnable has finished its Run() method.
|
||||||
|
void
|
||||||
|
IndexedDatabaseManager::OnOriginClearComplete(OriginClearRunnable* aRunnable)
|
||||||
|
{
|
||||||
|
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||||
|
NS_ASSERTION(aRunnable, "Null pointer!");
|
||||||
|
NS_ASSERTION(!aRunnable->mThread, "Thread should be null!");
|
||||||
|
NS_ASSERTION(aRunnable->mDatabasesWaiting.IsEmpty(), "Databases waiting?!");
|
||||||
|
NS_ASSERTION(aRunnable->mDelayedRunnables.IsEmpty(),
|
||||||
|
"Delayed runnables should have been dispatched already!");
|
||||||
|
|
||||||
|
if (!mOriginClearRunnables.RemoveElement(aRunnable)) {
|
||||||
|
NS_ERROR("Don't know anything about this runnable!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when AsyncUsageRunnable has finished its Run() method.
|
||||||
|
void
|
||||||
|
IndexedDatabaseManager::OnUsageCheckComplete(AsyncUsageRunnable* aRunnable)
|
||||||
|
{
|
||||||
|
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||||
|
NS_ASSERTION(aRunnable, "Null pointer!");
|
||||||
|
NS_ASSERTION(!aRunnable->mURI, "Should have been cleared!");
|
||||||
|
NS_ASSERTION(!aRunnable->mCallback, "Should have been cleared!");
|
||||||
|
|
||||||
|
if (!mUsageRunnables.RemoveElement(aRunnable)) {
|
||||||
|
NS_ERROR("Don't know anything about this runnable!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Waits until it is safe for a new database to be created with the given origin
|
||||||
|
// before dispatching the given runnable.
|
||||||
nsresult
|
nsresult
|
||||||
IndexedDatabaseManager::WaitForClearAndDispatch(const nsACString& aOrigin,
|
IndexedDatabaseManager::WaitForClearAndDispatch(const nsACString& aOrigin,
|
||||||
nsIRunnable* aRunnable)
|
nsIRunnable* aRunnable)
|
||||||
|
@ -197,12 +270,12 @@ IndexedDatabaseManager::WaitForClearAndDispatch(const nsACString& aOrigin,
|
||||||
|
|
||||||
// See if we're currently clearing database files for this origin. If so then
|
// See if we're currently clearing database files for this origin. If so then
|
||||||
// queue the runnable for later dispatch after we're done clearing.
|
// queue the runnable for later dispatch after we're done clearing.
|
||||||
PRUint32 count = mOriginClearData.Length();
|
PRUint32 count = mOriginClearRunnables.Length();
|
||||||
for (PRUint32 index = 0; index < count; index++) {
|
for (PRUint32 index = 0; index < count; index++) {
|
||||||
OriginClearData& data = mOriginClearData[index];
|
nsRefPtr<OriginClearRunnable>& data = mOriginClearRunnables[index];
|
||||||
if (data.origin == aOrigin) {
|
if (data->mOrigin == aOrigin) {
|
||||||
nsCOMPtr<nsIRunnable>* newPtr =
|
nsCOMPtr<nsIRunnable>* newPtr =
|
||||||
data.delayedRunnables.AppendElement(aRunnable);
|
data->mDelayedRunnables.AppendElement(aRunnable);
|
||||||
NS_ENSURE_TRUE(newPtr, NS_ERROR_OUT_OF_MEMORY);
|
NS_ENSURE_TRUE(newPtr, NS_ERROR_OUT_OF_MEMORY);
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
@ -218,71 +291,76 @@ NS_IMPL_ISUPPORTS2(IndexedDatabaseManager, nsIIndexedDatabaseManager,
|
||||||
nsIObserver)
|
nsIObserver)
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
IndexedDatabaseManager::GetUsageForURI(nsIURI* aURI,
|
IndexedDatabaseManager::GetUsageForURI(
|
||||||
PRUint64* _retval)
|
nsIURI* aURI,
|
||||||
|
nsIIndexedDatabaseUsageCallback* aCallback)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||||
|
|
||||||
NS_ENSURE_ARG_POINTER(aURI);
|
NS_ENSURE_ARG_POINTER(aURI);
|
||||||
|
NS_ENSURE_ARG_POINTER(aCallback);
|
||||||
|
|
||||||
// Figure out which origin we're dealing with.
|
// Figure out which origin we're dealing with.
|
||||||
nsCString origin;
|
nsCString origin;
|
||||||
nsresult rv = nsContentUtils::GetASCIIOrigin(aURI, origin);
|
nsresult rv = nsContentUtils::GetASCIIOrigin(aURI, origin);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
// Non-standard URIs can't create databases anyway, so return 0.
|
nsRefPtr<AsyncUsageRunnable> runnable =
|
||||||
|
new AsyncUsageRunnable(aURI, origin, aCallback);
|
||||||
|
|
||||||
|
nsRefPtr<AsyncUsageRunnable>* newRunnable =
|
||||||
|
mUsageRunnables.AppendElement(runnable);
|
||||||
|
NS_ENSURE_TRUE(newRunnable, NS_ERROR_OUT_OF_MEMORY);
|
||||||
|
|
||||||
|
// Non-standard URIs can't create databases anyway so fire the callback
|
||||||
|
// immediately.
|
||||||
if (origin.EqualsLiteral("null")) {
|
if (origin.EqualsLiteral("null")) {
|
||||||
*_retval = 0;
|
rv = NS_DispatchToCurrentThread(runnable);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the directory where we may be storing database files for this origin.
|
// See if we're currently clearing the databases for this origin. If so then
|
||||||
nsCOMPtr<nsIFile> directory;
|
// we pretend that we've already deleted everything.
|
||||||
rv = IDBFactory::GetDirectoryForOrigin(origin, getter_AddRefs(directory));
|
for (PRUint32 index = 0; index < mOriginClearRunnables.Length(); index++) {
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
if (mOriginClearRunnables[index]->mOrigin == origin) {
|
||||||
|
rv = NS_DispatchToCurrentThread(runnable);
|
||||||
PRBool exists;
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
rv = directory->Exists(&exists);
|
return NS_OK;
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
PRUint64 usage = 0;
|
|
||||||
|
|
||||||
// If the directory exists then enumerate all the files inside, adding up the
|
|
||||||
// sizes to get the final usage statistic.
|
|
||||||
if (exists) {
|
|
||||||
nsCOMPtr<nsISimpleEnumerator> entries;
|
|
||||||
rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
if (entries) {
|
|
||||||
PRBool hasMore;
|
|
||||||
while (NS_SUCCEEDED(entries->HasMoreElements(&hasMore)) && hasMore) {
|
|
||||||
nsCOMPtr<nsISupports> entry;
|
|
||||||
rv = entries->GetNext(getter_AddRefs(entry));
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
nsCOMPtr<nsIFile> file(do_QueryInterface(entry));
|
|
||||||
NS_ASSERTION(file, "Don't know what this is!");
|
|
||||||
|
|
||||||
PRInt64 fileSize;
|
|
||||||
rv = file->GetFileSize(&fileSize);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
NS_ASSERTION(fileSize > 0, "Negative size?!");
|
|
||||||
|
|
||||||
// Watch for overflow!
|
|
||||||
if (NS_UNLIKELY((LL_MAXINT - usage) <= PRUint64(fileSize))) {
|
|
||||||
NS_WARNING("Database sizes exceed max we can report!");
|
|
||||||
usage = LL_MAXINT;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
usage += fileSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*_retval = usage;
|
// Otherwise dispatch to the IO thread to actually compute the usage.
|
||||||
|
rv = mIOThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
IndexedDatabaseManager::CancelGetUsageForURI(
|
||||||
|
nsIURI* aURI,
|
||||||
|
nsIIndexedDatabaseUsageCallback* aCallback)
|
||||||
|
{
|
||||||
|
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||||
|
|
||||||
|
NS_ENSURE_ARG_POINTER(aURI);
|
||||||
|
NS_ENSURE_ARG_POINTER(aCallback);
|
||||||
|
|
||||||
|
// See if one of our pending callbacks matches both the URI and the callback
|
||||||
|
// given. Cancel an remove it if so.
|
||||||
|
for (PRUint32 index = 0; index < mUsageRunnables.Length(); index++) {
|
||||||
|
nsRefPtr<AsyncUsageRunnable>& runnable = mUsageRunnables[index];
|
||||||
|
|
||||||
|
PRBool equals;
|
||||||
|
nsresult rv = runnable->mURI->Equals(aURI, &equals);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
if (equals && SameCOMIdentity(aCallback, runnable->mCallback)) {
|
||||||
|
runnable->Cancel();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,9 +383,9 @@ IndexedDatabaseManager::ClearDatabasesForURI(nsIURI* aURI)
|
||||||
|
|
||||||
// If we're already clearing out files for this origin then return
|
// If we're already clearing out files for this origin then return
|
||||||
// immediately.
|
// immediately.
|
||||||
PRUint32 clearDataCount = mOriginClearData.Length();
|
PRUint32 clearDataCount = mOriginClearRunnables.Length();
|
||||||
for (PRUint32 index = 0; index < clearDataCount; index++) {
|
for (PRUint32 index = 0; index < clearDataCount; index++) {
|
||||||
if (mOriginClearData[index].origin == origin) {
|
if (mOriginClearRunnables[index]->mOrigin == origin) {
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -325,61 +403,37 @@ IndexedDatabaseManager::ClearDatabasesForURI(nsIURI* aURI)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make a new entry for this origin in mOriginClearData. Don't return early
|
nsRefPtr<OriginClearRunnable> runnable =
|
||||||
// while mOriginClearData has this origin in it!
|
new OriginClearRunnable(origin, mIOThread, liveDatabases);
|
||||||
OriginClearData* data = mOriginClearData.AppendElement();
|
|
||||||
NS_ENSURE_TRUE(data, NS_ERROR_OUT_OF_MEMORY);
|
|
||||||
|
|
||||||
data->origin = origin;
|
NS_ASSERTION(liveDatabases.IsEmpty(), "Should have swapped!");
|
||||||
|
|
||||||
if (!liveDatabases.IsEmpty()) {
|
// Make a new entry for this origin in mOriginClearRunnables.
|
||||||
PRUint32 count = liveDatabases.Length();
|
nsRefPtr<OriginClearRunnable>* newRunnable =
|
||||||
|
mOriginClearRunnables.AppendElement(runnable);
|
||||||
|
NS_ENSURE_TRUE(newRunnable, NS_ERROR_OUT_OF_MEMORY);
|
||||||
|
|
||||||
|
if (!runnable->mDatabasesWaiting.IsEmpty()) {
|
||||||
|
PRUint32 count = runnable->mDatabasesWaiting.Length();
|
||||||
|
|
||||||
// Invalidate all the live databases first.
|
// Invalidate all the live databases first.
|
||||||
for (PRUint32 index = 0; index < count; index++) {
|
for (PRUint32 index = 0; index < count; index++) {
|
||||||
liveDatabases[index]->Invalidate();
|
runnable->mDatabasesWaiting[index]->Invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now wait for them to finish.
|
// Now set up our callbacks so that we know when they have finished.
|
||||||
|
TransactionThreadPool* pool = TransactionThreadPool::Get();
|
||||||
for (PRUint32 index = 0; index < count; index++) {
|
for (PRUint32 index = 0; index < count; index++) {
|
||||||
liveDatabases[index]->WaitForConnectionReleased();
|
if (!pool->WaitForAllTransactionsToComplete(
|
||||||
}
|
runnable->mDatabasesWaiting[index],
|
||||||
}
|
OriginClearRunnable::DatabaseCompleteCallback,
|
||||||
|
runnable)) {
|
||||||
// Now that all our databases have released their connections to their files
|
NS_WARNING("Out of memory!");
|
||||||
// we can go ahead and delete the directory. Don't return early while
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
// mOriginClearData has this origin in it!
|
|
||||||
nsCOMPtr<nsIFile> directory;
|
|
||||||
rv = IDBFactory::GetDirectoryForOrigin(origin, getter_AddRefs(directory));
|
|
||||||
if (NS_SUCCEEDED(rv)) {
|
|
||||||
PRBool exists;
|
|
||||||
rv = directory->Exists(&exists);
|
|
||||||
if (NS_SUCCEEDED(rv) && exists) {
|
|
||||||
rv = directory->Remove(PR_TRUE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove this origin's entry from mOriginClearData. Dispatch any runnables
|
|
||||||
// that were queued along the way.
|
|
||||||
clearDataCount = mOriginClearData.Length();
|
|
||||||
for (PRUint32 clearDataIndex = 0; clearDataIndex < clearDataCount;
|
|
||||||
clearDataIndex++) {
|
|
||||||
OriginClearData& data = mOriginClearData[clearDataIndex];
|
|
||||||
if (data.origin == origin) {
|
|
||||||
nsTArray<nsCOMPtr<nsIRunnable> >& runnables = data.delayedRunnables;
|
|
||||||
PRUint32 runnableCount = runnables.Length();
|
|
||||||
for (PRUint32 runnableIndex = 0; runnableIndex < runnableCount;
|
|
||||||
runnableIndex++) {
|
|
||||||
NS_DispatchToCurrentThread(runnables[runnableIndex]);
|
|
||||||
}
|
}
|
||||||
mOriginClearData.RemoveElementAt(clearDataIndex);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now we can finally return errors.
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -390,31 +444,265 @@ IndexedDatabaseManager::Observe(nsISupports* aSubject,
|
||||||
{
|
{
|
||||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||||
|
|
||||||
if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
|
if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID)) {
|
||||||
// Setting this flag prevents the servic from being recreated and prevents
|
// Setting this flag prevents the servic from being recreated and prevents
|
||||||
// further databases from being created.
|
// further databases from being created.
|
||||||
gShutdown = true;
|
gShutdown = true;
|
||||||
|
|
||||||
// Grab all live databases, for all origins. Need references to keep them
|
// Make sure to join with our IO thread.
|
||||||
// alive while we wait on them.
|
if (NS_FAILED(mIOThread->Shutdown())) {
|
||||||
nsAutoTArray<nsRefPtr<IDBDatabase>, 50> liveDatabases;
|
NS_WARNING("Failed to shutdown IO thread!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kick off the shutdown timer.
|
||||||
|
if (NS_FAILED(mShutdownTimer->Init(this, DEFAULT_SHUTDOWN_TIMER_MS,
|
||||||
|
nsITimer::TYPE_ONE_SHOT))) {
|
||||||
|
NS_WARNING("Failed to initialize shutdown timer!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will spin the event loop while we wait on all the database threads
|
||||||
|
// to close. Our timer may fire during that loop.
|
||||||
|
TransactionThreadPool::Shutdown();
|
||||||
|
|
||||||
|
// Cancel the timer regardless of whether it actually fired.
|
||||||
|
if (NS_FAILED(mShutdownTimer->Cancel())) {
|
||||||
|
NS_WARNING("Failed to cancel shutdown timer!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC)) {
|
||||||
|
NS_WARNING("Some database operations are taking longer than expected "
|
||||||
|
"during shutdown and will be aborted!");
|
||||||
|
|
||||||
|
// Grab all live databases, for all origins.
|
||||||
|
nsAutoTArray<IDBDatabase*, 50> liveDatabases;
|
||||||
mLiveDatabases.EnumerateRead(EnumerateToTArray, &liveDatabases);
|
mLiveDatabases.EnumerateRead(EnumerateToTArray, &liveDatabases);
|
||||||
|
|
||||||
// Wait for all live databases to finish.
|
// Invalidate them all.
|
||||||
if (!liveDatabases.IsEmpty()) {
|
if (!liveDatabases.IsEmpty()) {
|
||||||
PRUint32 count = liveDatabases.Length();
|
PRUint32 count = liveDatabases.Length();
|
||||||
for (PRUint32 index = 0; index < count; index++) {
|
for (PRUint32 index = 0; index < count; index++) {
|
||||||
liveDatabases[index]->WaitForConnectionReleased();
|
liveDatabases[index]->Invalidate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mLiveDatabases.Clear();
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
// This may kill us.
|
if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
|
||||||
gInstance->Release();
|
// We're dying now.
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_NOTREACHED("Unknown topic!");
|
NS_NOTREACHED("Unknown topic!");
|
||||||
return NS_ERROR_UNEXPECTED;
|
return NS_ERROR_UNEXPECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Called by the TransactionThreadPool when the given database has completed all
|
||||||
|
// of its transactions.
|
||||||
|
void
|
||||||
|
IndexedDatabaseManager::OriginClearRunnable::OnDatabaseComplete(
|
||||||
|
IDBDatabase* aDatabase)
|
||||||
|
{
|
||||||
|
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||||
|
NS_ASSERTION(aDatabase, "Null pointer!");
|
||||||
|
NS_ASSERTION(mThread, "This shouldn't be cleared yet!");
|
||||||
|
|
||||||
|
// Remove the database from the list of databases that we're waiting on.
|
||||||
|
if (!mDatabasesWaiting.RemoveElement(aDatabase)) {
|
||||||
|
NS_ERROR("Don't know anything about this database!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now dispatch this runnable to the IO thread if the list is empty.
|
||||||
|
if (mDatabasesWaiting.IsEmpty()) {
|
||||||
|
if (NS_FAILED(mThread->Dispatch(this, NS_DISPATCH_NORMAL))) {
|
||||||
|
NS_WARNING("Can't dispatch to IO thread!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// We no longer need to keep the thread alive.
|
||||||
|
mThread = nsnull;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMPL_THREADSAFE_ISUPPORTS1(IndexedDatabaseManager::OriginClearRunnable,
|
||||||
|
nsIRunnable)
|
||||||
|
|
||||||
|
// Runs twice, first on the IO thread, then again on the main thread. While on
|
||||||
|
// the IO thread the runnable will actually remove the origin's database files
|
||||||
|
// and the directory that contains them before dispatching itself back to the
|
||||||
|
// main thread. When on the main thread the runnable will dispatch any queued
|
||||||
|
// runnables and then notify the IndexedDatabaseManager that the job has been
|
||||||
|
// completed.
|
||||||
|
NS_IMETHODIMP
|
||||||
|
IndexedDatabaseManager::OriginClearRunnable::Run()
|
||||||
|
{
|
||||||
|
if (NS_IsMainThread()) {
|
||||||
|
NS_ASSERTION(!mThread, "Should have been cleared already!");
|
||||||
|
|
||||||
|
// Dispatch any queued runnables that we collected while we were waiting.
|
||||||
|
for (PRUint32 index = 0; index < mDelayedRunnables.Length(); index++) {
|
||||||
|
if (NS_FAILED(NS_DispatchToCurrentThread(mDelayedRunnables[index]))) {
|
||||||
|
NS_WARNING("Failed to dispatch delayed runnable!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mDelayedRunnables.Clear();
|
||||||
|
|
||||||
|
// Tell the IndexedDatabaseManager that we're done.
|
||||||
|
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
|
||||||
|
if (mgr) {
|
||||||
|
mgr->OnOriginClearComplete(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the directory that contains all our databases.
|
||||||
|
nsCOMPtr<nsIFile> directory;
|
||||||
|
nsresult rv = IDBFactory::GetDirectoryForOrigin(mOrigin,
|
||||||
|
getter_AddRefs(directory));
|
||||||
|
if (NS_SUCCEEDED(rv)) {
|
||||||
|
PRBool exists;
|
||||||
|
rv = directory->Exists(&exists);
|
||||||
|
if (NS_SUCCEEDED(rv) && exists) {
|
||||||
|
rv = directory->Remove(PR_TRUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
IndexedDatabaseManager::AsyncUsageRunnable::AsyncUsageRunnable(
|
||||||
|
nsIURI* aURI,
|
||||||
|
const nsACString& aOrigin,
|
||||||
|
nsIIndexedDatabaseUsageCallback* aCallback)
|
||||||
|
: mURI(aURI),
|
||||||
|
mOrigin(aOrigin),
|
||||||
|
mCallback(aCallback),
|
||||||
|
mUsage(0),
|
||||||
|
mCanceled(0)
|
||||||
|
{
|
||||||
|
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||||
|
NS_ASSERTION(aURI, "Null pointer!");
|
||||||
|
NS_ASSERTION(!aOrigin.IsEmpty(), "Empty origin!");
|
||||||
|
NS_ASSERTION(aCallback, "Null pointer!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets the canceled flag so that the callback is never called.
|
||||||
|
void
|
||||||
|
IndexedDatabaseManager::AsyncUsageRunnable::Cancel()
|
||||||
|
{
|
||||||
|
if (PR_AtomicSet(&mCanceled, 1)) {
|
||||||
|
NS_ERROR("Canceled more than once?!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Runs twice, first on the IO thread, then again on the main thread. While on
|
||||||
|
// the IO thread the runnable will calculate the size of all files in the
|
||||||
|
// origin's directory before dispatching itself back to the main thread. When on
|
||||||
|
// the main thread the runnable will call the callback and then notify the
|
||||||
|
// IndexedDatabaseManager that the job has been completed.
|
||||||
|
nsresult
|
||||||
|
IndexedDatabaseManager::AsyncUsageRunnable::RunInternal()
|
||||||
|
{
|
||||||
|
if (NS_IsMainThread()) {
|
||||||
|
// Call the callback unless we were canceled.
|
||||||
|
if (!mCanceled) {
|
||||||
|
mCallback->OnUsageResult(mURI, mUsage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up.
|
||||||
|
mURI = nsnull;
|
||||||
|
mCallback = nsnull;
|
||||||
|
|
||||||
|
// And tell the IndexedDatabaseManager that we're done.
|
||||||
|
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
|
||||||
|
if (mgr) {
|
||||||
|
mgr->OnUsageCheckComplete(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mCanceled) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the directory that contains all the database files we care about.
|
||||||
|
nsCOMPtr<nsIFile> directory;
|
||||||
|
nsresult rv = IDBFactory::GetDirectoryForOrigin(mOrigin,
|
||||||
|
getter_AddRefs(directory));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
PRBool exists;
|
||||||
|
rv = directory->Exists(&exists);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
// If the directory exists then enumerate all the files inside, adding up the
|
||||||
|
// sizes to get the final usage statistic.
|
||||||
|
if (exists && !mCanceled) {
|
||||||
|
nsCOMPtr<nsISimpleEnumerator> entries;
|
||||||
|
rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
if (entries) {
|
||||||
|
PRBool hasMore;
|
||||||
|
while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) &&
|
||||||
|
hasMore && !mCanceled) {
|
||||||
|
nsCOMPtr<nsISupports> entry;
|
||||||
|
rv = entries->GetNext(getter_AddRefs(entry));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
nsCOMPtr<nsIFile> file(do_QueryInterface(entry));
|
||||||
|
NS_ASSERTION(file, "Don't know what this is!");
|
||||||
|
|
||||||
|
PRInt64 fileSize;
|
||||||
|
rv = file->GetFileSize(&fileSize);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
NS_ASSERTION(fileSize > 0, "Negative size?!");
|
||||||
|
|
||||||
|
// Watch for overflow!
|
||||||
|
if (NS_UNLIKELY((LL_MAXINT - mUsage) <= PRUint64(fileSize))) {
|
||||||
|
NS_WARNING("Database sizes exceed max we can report!");
|
||||||
|
mUsage = LL_MAXINT;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mUsage += fileSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMPL_THREADSAFE_ISUPPORTS1(IndexedDatabaseManager::AsyncUsageRunnable,
|
||||||
|
nsIRunnable)
|
||||||
|
|
||||||
|
// Calls the RunInternal method and makes sure that we always dispatch to the
|
||||||
|
// main thread in case of an error.
|
||||||
|
NS_IMETHODIMP
|
||||||
|
IndexedDatabaseManager::AsyncUsageRunnable::Run()
|
||||||
|
{
|
||||||
|
nsresult rv = RunInternal();
|
||||||
|
|
||||||
|
if (!NS_IsMainThread()) {
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
mUsage = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) {
|
||||||
|
NS_WARNING("Failed to dispatch to main thread!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
|
@ -41,9 +41,13 @@
|
||||||
#define mozilla_dom_indexeddb_indexeddatabasemanager_h__
|
#define mozilla_dom_indexeddb_indexeddatabasemanager_h__
|
||||||
|
|
||||||
#include "mozilla/dom/indexedDB/IndexedDatabase.h"
|
#include "mozilla/dom/indexedDB/IndexedDatabase.h"
|
||||||
|
#include "mozilla/dom/indexedDB/IDBDatabase.h"
|
||||||
|
|
||||||
#include "nsIIndexedDatabaseManager.h"
|
#include "nsIIndexedDatabaseManager.h"
|
||||||
#include "nsIObserver.h"
|
#include "nsIObserver.h"
|
||||||
|
#include "nsIRunnable.h"
|
||||||
|
#include "nsIThread.h"
|
||||||
|
#include "nsIURI.h"
|
||||||
|
|
||||||
#include "nsClassHashtable.h"
|
#include "nsClassHashtable.h"
|
||||||
#include "nsHashKeys.h"
|
#include "nsHashKeys.h"
|
||||||
|
@ -51,23 +55,23 @@
|
||||||
#define INDEXEDDB_MANAGER_CONTRACTID \
|
#define INDEXEDDB_MANAGER_CONTRACTID \
|
||||||
"@mozilla.org/dom/indexeddb/manager;1"
|
"@mozilla.org/dom/indexeddb/manager;1"
|
||||||
|
|
||||||
class nsIRunnable;
|
class nsITimer;
|
||||||
|
|
||||||
BEGIN_INDEXEDDB_NAMESPACE
|
BEGIN_INDEXEDDB_NAMESPACE
|
||||||
|
|
||||||
class IDBDatabase;
|
|
||||||
|
|
||||||
class IndexedDatabaseManager : public nsIIndexedDatabaseManager,
|
class IndexedDatabaseManager : public nsIIndexedDatabaseManager,
|
||||||
public nsIObserver
|
public nsIObserver
|
||||||
{
|
{
|
||||||
friend class IDBDatabase;
|
friend class IDBDatabase;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Returns an owning reference!
|
static already_AddRefed<IndexedDatabaseManager> GetOrCreate();
|
||||||
static IndexedDatabaseManager* GetOrCreateInstance();
|
|
||||||
|
|
||||||
// Returns a non-owning reference.
|
// Returns a non-owning reference.
|
||||||
static IndexedDatabaseManager* GetInstance();
|
static IndexedDatabaseManager* Get();
|
||||||
|
|
||||||
|
// Returns an owning reference! No one should call this but the factory.
|
||||||
|
static IndexedDatabaseManager* FactoryCreate();
|
||||||
|
|
||||||
NS_DECL_ISUPPORTS
|
NS_DECL_ISUPPORTS
|
||||||
NS_DECL_NSIINDEXEDDATABASEMANAGER
|
NS_DECL_NSIINDEXEDDATABASEMANAGER
|
||||||
|
@ -83,17 +87,78 @@ private:
|
||||||
bool RegisterDatabase(IDBDatabase* aDatabase);
|
bool RegisterDatabase(IDBDatabase* aDatabase);
|
||||||
void UnregisterDatabase(IDBDatabase* aDatabase);
|
void UnregisterDatabase(IDBDatabase* aDatabase);
|
||||||
|
|
||||||
struct OriginClearData
|
// Responsible for clearing the database files for a particular origin on the
|
||||||
|
// IO thread.
|
||||||
|
class OriginClearRunnable : public nsIRunnable
|
||||||
{
|
{
|
||||||
nsCString origin;
|
public:
|
||||||
nsTArray<nsCOMPtr<nsIRunnable> > delayedRunnables;
|
NS_DECL_ISUPPORTS
|
||||||
|
NS_DECL_NSIRUNNABLE
|
||||||
|
|
||||||
|
static void DatabaseCompleteCallback(IDBDatabase* aDatabase,
|
||||||
|
void* aClosure)
|
||||||
|
{
|
||||||
|
nsRefPtr<OriginClearRunnable> runnable =
|
||||||
|
static_cast<OriginClearRunnable*>(aClosure);
|
||||||
|
runnable->OnDatabaseComplete(aDatabase);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnDatabaseComplete(IDBDatabase* aDatabase);
|
||||||
|
|
||||||
|
OriginClearRunnable(const nsACString& aOrigin,
|
||||||
|
nsIThread* aThread,
|
||||||
|
nsTArray<nsRefPtr<IDBDatabase> >& aDatabasesWaiting)
|
||||||
|
: mOrigin(aOrigin),
|
||||||
|
mThread(aThread)
|
||||||
|
{
|
||||||
|
mDatabasesWaiting.SwapElements(aDatabasesWaiting);
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCString mOrigin;
|
||||||
|
nsCOMPtr<nsIThread> mThread;
|
||||||
|
nsTArray<nsRefPtr<IDBDatabase> > mDatabasesWaiting;
|
||||||
|
nsTArray<nsCOMPtr<nsIRunnable> > mDelayedRunnables;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline void OnOriginClearComplete(OriginClearRunnable* aRunnable);
|
||||||
|
|
||||||
|
// Responsible for calculating the amount of space taken up by databases of a
|
||||||
|
// certain origin. Runs on the IO thread.
|
||||||
|
class AsyncUsageRunnable : public nsIRunnable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NS_DECL_ISUPPORTS
|
||||||
|
NS_DECL_NSIRUNNABLE
|
||||||
|
|
||||||
|
AsyncUsageRunnable(nsIURI* aURI,
|
||||||
|
const nsACString& aOrigin,
|
||||||
|
nsIIndexedDatabaseUsageCallback* aCallback);
|
||||||
|
|
||||||
|
void Cancel();
|
||||||
|
|
||||||
|
inline nsresult RunInternal();
|
||||||
|
|
||||||
|
nsCOMPtr<nsIURI> mURI;
|
||||||
|
nsCString mOrigin;
|
||||||
|
nsCOMPtr<nsIIndexedDatabaseUsageCallback> mCallback;
|
||||||
|
PRUint64 mUsage;
|
||||||
|
PRInt32 mCanceled;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void OnUsageCheckComplete(AsyncUsageRunnable* aRunnable);
|
||||||
|
|
||||||
// Maintains a list of live databases per origin.
|
// Maintains a list of live databases per origin.
|
||||||
nsClassHashtable<nsCStringHashKey, nsTArray<IDBDatabase*> > mLiveDatabases;
|
nsClassHashtable<nsCStringHashKey, nsTArray<IDBDatabase*> > mLiveDatabases;
|
||||||
|
|
||||||
// Maintains a list of origins that are currently being cleared.
|
// Maintains a list of origins that are currently being cleared.
|
||||||
nsAutoTArray<OriginClearData, 1> mOriginClearData;
|
nsAutoTArray<nsRefPtr<OriginClearRunnable>, 1> mOriginClearRunnables;
|
||||||
|
|
||||||
|
// Maintains a list of origins that we're currently enumerating to gather
|
||||||
|
// usage statistics.
|
||||||
|
nsAutoTArray<nsRefPtr<AsyncUsageRunnable>, 1> mUsageRunnables;
|
||||||
|
|
||||||
|
nsCOMPtr<nsIThread> mIOThread;
|
||||||
|
nsCOMPtr<nsITimer> mShutdownTimer;
|
||||||
};
|
};
|
||||||
|
|
||||||
END_INDEXEDDB_NAMESPACE
|
END_INDEXEDDB_NAMESPACE
|
||||||
|
|
|
@ -86,6 +86,7 @@ EXPORTS_mozilla/dom/indexedDB = \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
LOCAL_INCLUDES = \
|
LOCAL_INCLUDES = \
|
||||||
|
-I$(topsrcdir)/xpcom/build \
|
||||||
-I$(topsrcdir)/dom/base \
|
-I$(topsrcdir)/dom/base \
|
||||||
-I$(topsrcdir)/dom/src/storage \
|
-I$(topsrcdir)/dom/src/storage \
|
||||||
-I$(topsrcdir)/content/base/src \
|
-I$(topsrcdir)/content/base/src \
|
||||||
|
|
|
@ -116,7 +116,8 @@ TransactionThreadPool::TransactionThreadPool()
|
||||||
TransactionThreadPool::~TransactionThreadPool()
|
TransactionThreadPool::~TransactionThreadPool()
|
||||||
{
|
{
|
||||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||||
NS_ASSERTION(!gInstance, "More than one instance!");
|
NS_ASSERTION(gInstance == this, "Different instances!");
|
||||||
|
gInstance = nsnull;
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
|
@ -125,20 +126,12 @@ TransactionThreadPool::GetOrCreate()
|
||||||
{
|
{
|
||||||
if (!gInstance && !gShutdown) {
|
if (!gInstance && !gShutdown) {
|
||||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||||
nsRefPtr<TransactionThreadPool> pool(new TransactionThreadPool());
|
nsAutoPtr<TransactionThreadPool> pool(new TransactionThreadPool());
|
||||||
|
|
||||||
nsresult rv = pool->Init();
|
nsresult rv = pool->Init();
|
||||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||||
|
|
||||||
nsCOMPtr<nsIObserverService> obs =
|
gInstance = pool.forget();
|
||||||
do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
|
|
||||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
|
||||||
|
|
||||||
rv = obs->AddObserver(pool, "xpcom-shutdown-threads", PR_FALSE);
|
|
||||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
|
||||||
|
|
||||||
// The observer service now owns us.
|
|
||||||
gInstance = pool;
|
|
||||||
}
|
}
|
||||||
return gInstance;
|
return gInstance;
|
||||||
}
|
}
|
||||||
|
@ -162,6 +155,7 @@ TransactionThreadPool::Shutdown()
|
||||||
if (NS_FAILED(gInstance->Cleanup())) {
|
if (NS_FAILED(gInstance->Cleanup())) {
|
||||||
NS_WARNING("Failed to shutdown thread pool!");
|
NS_WARNING("Failed to shutdown thread pool!");
|
||||||
}
|
}
|
||||||
|
delete gInstance;
|
||||||
gInstance = nsnull;
|
gInstance = nsnull;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -247,6 +241,19 @@ TransactionThreadPool::FinishTransaction(IDBTransaction* aTransaction)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
mTransactionsInProgress.Remove(databaseId);
|
mTransactionsInProgress.Remove(databaseId);
|
||||||
|
|
||||||
|
// See if we need to fire any complete callbacks.
|
||||||
|
for (PRUint32 index = 0; index < mCompleteRunnables.Length(); index++) {
|
||||||
|
nsRefPtr<DatabaseCompleteCallbackRunnable>& runnable =
|
||||||
|
mCompleteRunnables[index];
|
||||||
|
if (runnable->mDatabase == aTransaction->Database()) {
|
||||||
|
if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
|
||||||
|
NS_WARNING("Failed to dispatch to current thread?!");
|
||||||
|
}
|
||||||
|
mCompleteRunnables.RemoveElementAt(index);
|
||||||
|
index--;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// We need to rebuild the locked object store list.
|
// We need to rebuild the locked object store list.
|
||||||
|
@ -483,37 +490,37 @@ TransactionThreadPool::Dispatch(IDBTransaction* aTransaction,
|
||||||
return mThreadPool->Dispatch(transactionInfo->queue, NS_DISPATCH_NORMAL);
|
return mThreadPool->Dispatch(transactionInfo->queue, NS_DISPATCH_NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
bool
|
||||||
TransactionThreadPool::WaitForAllTransactionsToComplete(IDBDatabase* aDatabase)
|
TransactionThreadPool::WaitForAllTransactionsToComplete(
|
||||||
|
IDBDatabase* aDatabase,
|
||||||
|
DatabaseCompleteCallback aCallback,
|
||||||
|
void* aUserData)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||||
|
NS_ASSERTION(aDatabase, "Null pointer!");
|
||||||
|
NS_ASSERTION(aCallback, "Null pointer!");
|
||||||
|
|
||||||
|
nsRefPtr<DatabaseCompleteCallbackRunnable> runnable =
|
||||||
|
new DatabaseCompleteCallbackRunnable(aDatabase, aCallback, aUserData);
|
||||||
|
|
||||||
|
// See if this database has any active transactions. If not then we can
|
||||||
|
// dispatch the callback immediately.
|
||||||
const PRUint32 databaseId = aDatabase->Id();
|
const PRUint32 databaseId = aDatabase->Id();
|
||||||
nsIThread* currentThread = NS_GetCurrentThread();
|
if (!mTransactionsInProgress.Get(databaseId, nsnull)) {
|
||||||
|
if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
|
||||||
// As soon as all of the transactions for this database are complete its
|
NS_WARNING("Failed to dispatch to current thread?!");
|
||||||
// entry in mTransactionsInProgress will be removed, so just loop while
|
return false;
|
||||||
// checking.
|
|
||||||
while (mTransactionsInProgress.Get(databaseId, nsnull)) {
|
|
||||||
if (NS_FAILED(NS_ProcessNextEvent(currentThread, PR_TRUE))) {
|
|
||||||
NS_WARNING("Failed to process next event?!");
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMPL_THREADSAFE_ISUPPORTS1(TransactionThreadPool, nsIObserver)
|
// The database has active transactions, wait and fire the callback later.
|
||||||
|
if (!mCompleteRunnables.AppendElement(runnable)) {
|
||||||
|
NS_WARNING("Out of memory!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
return true;
|
||||||
TransactionThreadPool::Observe(nsISupports* /* aSubject */,
|
|
||||||
const char* aTopic,
|
|
||||||
const PRUnichar* /* aData */)
|
|
||||||
{
|
|
||||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
|
||||||
NS_ASSERTION(!strcmp("xpcom-shutdown-threads", aTopic), "Wrong topic!");
|
|
||||||
|
|
||||||
Shutdown();
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TransactionThreadPool::
|
TransactionThreadPool::
|
||||||
|
@ -637,3 +644,18 @@ FinishTransactionRunnable::Run()
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMPL_ISUPPORTS1(TransactionThreadPool::DatabaseCompleteCallbackRunnable,
|
||||||
|
nsIRunnable)
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
TransactionThreadPool::DatabaseCompleteCallbackRunnable::Run()
|
||||||
|
{
|
||||||
|
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||||
|
|
||||||
|
// Call our callback.
|
||||||
|
nsRefPtr<IDBDatabase> database;
|
||||||
|
database.swap(mDatabase);
|
||||||
|
mCallback(database, mUserData);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
#include "IndexedDatabase.h"
|
#include "IndexedDatabase.h"
|
||||||
|
|
||||||
#include "nsIObserver.h"
|
#include "nsIObserver.h"
|
||||||
|
#include "nsIRunnable.h"
|
||||||
|
|
||||||
#include "mozilla/Mutex.h"
|
#include "mozilla/Mutex.h"
|
||||||
#include "mozilla/CondVar.h"
|
#include "mozilla/CondVar.h"
|
||||||
|
@ -53,7 +54,6 @@
|
||||||
|
|
||||||
#include "IDBTransaction.h"
|
#include "IDBTransaction.h"
|
||||||
|
|
||||||
class nsIRunnable;
|
|
||||||
class nsIThreadPool;
|
class nsIThreadPool;
|
||||||
|
|
||||||
BEGIN_INDEXEDDB_NAMESPACE
|
BEGIN_INDEXEDDB_NAMESPACE
|
||||||
|
@ -61,17 +61,18 @@ BEGIN_INDEXEDDB_NAMESPACE
|
||||||
class FinishTransactionRunnable;
|
class FinishTransactionRunnable;
|
||||||
class QueuedDispatchInfo;
|
class QueuedDispatchInfo;
|
||||||
|
|
||||||
class TransactionThreadPool : public nsIObserver
|
class TransactionThreadPool
|
||||||
{
|
{
|
||||||
|
friend class nsAutoPtr<TransactionThreadPool>;
|
||||||
friend class FinishTransactionRunnable;
|
friend class FinishTransactionRunnable;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
NS_DECL_ISUPPORTS
|
|
||||||
NS_DECL_NSIOBSERVER
|
|
||||||
|
|
||||||
// returns a non-owning ref!
|
// returns a non-owning ref!
|
||||||
static TransactionThreadPool* GetOrCreate();
|
static TransactionThreadPool* GetOrCreate();
|
||||||
|
|
||||||
|
// returns a non-owning ref!
|
||||||
static TransactionThreadPool* Get();
|
static TransactionThreadPool* Get();
|
||||||
|
|
||||||
static void Shutdown();
|
static void Shutdown();
|
||||||
|
|
||||||
nsresult Dispatch(IDBTransaction* aTransaction,
|
nsresult Dispatch(IDBTransaction* aTransaction,
|
||||||
|
@ -79,7 +80,12 @@ public:
|
||||||
bool aFinish,
|
bool aFinish,
|
||||||
nsIRunnable* aFinishRunnable);
|
nsIRunnable* aFinishRunnable);
|
||||||
|
|
||||||
void WaitForAllTransactionsToComplete(IDBDatabase* aDatabase);
|
typedef void (*DatabaseCompleteCallback)(IDBDatabase* aDatabase,
|
||||||
|
void* aClosure);
|
||||||
|
|
||||||
|
bool WaitForAllTransactionsToComplete(IDBDatabase* aDatabase,
|
||||||
|
DatabaseCompleteCallback aCallback,
|
||||||
|
void* aUserData);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
class TransactionQueue : public nsIRunnable
|
class TransactionQueue : public nsIRunnable
|
||||||
|
@ -136,6 +142,28 @@ protected:
|
||||||
bool finish;
|
bool finish;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class DatabaseCompleteCallbackRunnable : public nsIRunnable
|
||||||
|
{
|
||||||
|
friend class TransactionThreadPool;
|
||||||
|
|
||||||
|
public:
|
||||||
|
NS_DECL_ISUPPORTS
|
||||||
|
NS_DECL_NSIRUNNABLE
|
||||||
|
|
||||||
|
DatabaseCompleteCallbackRunnable(IDBDatabase* aDatabase,
|
||||||
|
DatabaseCompleteCallback aCallback,
|
||||||
|
void* aUserData)
|
||||||
|
: mDatabase(aDatabase),
|
||||||
|
mCallback(aCallback),
|
||||||
|
mUserData(aUserData)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
private:
|
||||||
|
nsRefPtr<IDBDatabase> mDatabase;
|
||||||
|
DatabaseCompleteCallback mCallback;
|
||||||
|
void* mUserData;
|
||||||
|
};
|
||||||
|
|
||||||
TransactionThreadPool();
|
TransactionThreadPool();
|
||||||
~TransactionThreadPool();
|
~TransactionThreadPool();
|
||||||
|
|
||||||
|
@ -160,6 +188,8 @@ protected:
|
||||||
mTransactionsInProgress;
|
mTransactionsInProgress;
|
||||||
|
|
||||||
nsTArray<QueuedDispatchInfo> mDelayedDispatchQueue;
|
nsTArray<QueuedDispatchInfo> mDelayedDispatchQueue;
|
||||||
|
|
||||||
|
nsTArray<nsRefPtr<DatabaseCompleteCallbackRunnable> > mCompleteRunnables;
|
||||||
};
|
};
|
||||||
|
|
||||||
END_INDEXEDDB_NAMESPACE
|
END_INDEXEDDB_NAMESPACE
|
||||||
|
|
|
@ -41,10 +41,50 @@
|
||||||
|
|
||||||
interface nsIURI;
|
interface nsIURI;
|
||||||
|
|
||||||
[scriptable, uuid(dba13d2e-ea4f-4011-9b66-a8b0e61a7f84)]
|
[scriptable, function, uuid(17675af5-0569-4f5b-987f-ff4bb60f73ee)]
|
||||||
|
interface nsIIndexedDatabaseUsageCallback : nsISupports
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void onUsageResult(in nsIURI aURI,
|
||||||
|
in unsigned long long aUsage);
|
||||||
|
};
|
||||||
|
|
||||||
|
[scriptable, uuid(415f5684-6c84-4a8b-b777-d01f5df778f2)]
|
||||||
interface nsIIndexedDatabaseManager : nsISupports
|
interface nsIIndexedDatabaseManager : nsISupports
|
||||||
{
|
{
|
||||||
unsigned long long getUsageForURI(in nsIURI aURI);
|
/**
|
||||||
|
* Schedules an asynchronous callback that will return the total amount of
|
||||||
|
* disk space being used by databases for the given origin.
|
||||||
|
*
|
||||||
|
* @param aURI
|
||||||
|
* The URI whose usage is being queried.
|
||||||
|
* @param aCallback
|
||||||
|
* The callback that will be called when the usage is available.
|
||||||
|
*/
|
||||||
|
void getUsageForURI(in nsIURI aURI,
|
||||||
|
in nsIIndexedDatabaseUsageCallback aCallback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancels an asynchronous usage check initiated by a previous call to
|
||||||
|
* getUsageForURI().
|
||||||
|
*
|
||||||
|
* @param aURI
|
||||||
|
* The URI whose usage is being queried.
|
||||||
|
* @param aCallback
|
||||||
|
* The callback that will be called when the usage is available.
|
||||||
|
*/
|
||||||
|
void cancelGetUsageForURI(in nsIURI aURI,
|
||||||
|
in nsIIndexedDatabaseUsageCallback aCallback);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all databases stored for the given URI. The files may not be
|
||||||
|
* deleted immediately depending on prohibitive concurrent operations.
|
||||||
|
*
|
||||||
|
* @param aURI
|
||||||
|
* The URI whose databases are to be cleared.
|
||||||
|
*/
|
||||||
void clearDatabasesForURI(in nsIURI aURI);
|
void clearDatabasesForURI(in nsIURI aURI);
|
||||||
};
|
};
|
||||||
|
|
|
@ -325,7 +325,7 @@ NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsDOMStorageManager,
|
||||||
nsDOMStorageManager::GetInstance)
|
nsDOMStorageManager::GetInstance)
|
||||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsChannelPolicy)
|
NS_GENERIC_FACTORY_CONSTRUCTOR(nsChannelPolicy)
|
||||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(IndexedDatabaseManager,
|
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(IndexedDatabaseManager,
|
||||||
IndexedDatabaseManager::GetOrCreateInstance)
|
IndexedDatabaseManager::FactoryCreate)
|
||||||
#if defined(XP_UNIX) || \
|
#if defined(XP_UNIX) || \
|
||||||
defined(_WINDOWS) || \
|
defined(_WINDOWS) || \
|
||||||
defined(machintosh) || \
|
defined(machintosh) || \
|
||||||
|
|
Загрузка…
Ссылка в новой задаче