Bug 594583 - 'IndexedDB: Add some UI to clear IndexedDB databases'. r=sicking+gavin. a=beta6blocking.

This commit is contained in:
Ben Turner 2010-09-10 12:12:11 -07:00
Родитель 9959033066
Коммит c9978327b5
18 изменённых файлов: 814 добавлений и 7 удалений

Просмотреть файл

@ -89,11 +89,13 @@
<command id="cmd_cookieDef" oncommand="onCheckboxClick('cookie');"/>
<command id="cmd_installDef" oncommand="onCheckboxClick('install');"/>
<command id="cmd_geoDef" oncommand="onCheckboxClick('geo');"/>
<command id="cmd_indexedDBDef" oncommand="onCheckboxClick('indexedDB');"/>
<command id="cmd_imageToggle" oncommand="onRadioClick('image');"/>
<command id="cmd_popupToggle" oncommand="onRadioClick('popup');"/>
<command id="cmd_cookieToggle" oncommand="onRadioClick('cookie');"/>
<command id="cmd_installToggle" oncommand="onRadioClick('install');"/>
<command id="cmd_geoToggle" oncommand="onRadioClick('geo');"/>
<command id="cmd_indexedDBToggle" oncommand="onRadioClick('indexedDB');"/>
</commandset>
<keyset>
@ -375,6 +377,23 @@
</radiogroup>
</hbox>
</vbox>
<vbox class="permission">
<label class="permissionLabel" id="permIndexedDBLabel"
value="&permIndexedDB;" control="indexedDBRadioGroup"/>
<hbox role="group" aria-labelledby="permIndexedDBLabel">
<checkbox id="indexedDBDef" command="cmd_indexedDBDef" label="&permAskAlways;"/>
<spacer flex="1"/>
<vbox pack="center">
<label id="indexedDBStatus" control="indexedDBClear"/>
</vbox>
<button id="indexedDBClear" label="&permClearStorage;"
accesskey="&permClearStorage.accesskey;" onclick="onIndexedDBClear();"/>
<radiogroup id="indexedDBRadioGroup" orient="horizontal">
<radio id="indexedDB#1" command="cmd_indexedDBToggle" label="&permAllow;"/>
<radio id="indexedDB#2" command="cmd_indexedDBToggle" label="&permBlock;"/>
</radiogroup>
</hbox>
</vbox>
</vbox>
</vbox>

Просмотреть файл

@ -36,6 +36,10 @@
const ALLOW = nsIPermissionManager.ALLOW_ACTION; // 1
const BLOCK = nsIPermissionManager.DENY_ACTION; // 2
const SESSION = nsICookiePermission.ACCESS_SESSION;// 8
const nsIIndexedDatabaseManager =
Components.interfaces.nsIIndexedDatabaseManager;
var gPermURI;
var gPrefs;
@ -73,7 +77,11 @@ var gPermObj = {
},
geo: function getGeoDefaultPermissions()
{
return BLOCK;
return BLOCK;
},
indexedDB: function getIndexedDBDefaultPermissions()
{
return BLOCK;
}
};
@ -137,6 +145,10 @@ function initRow(aPartId)
perm = gPermObj[aPartId]();
}
setRadioState(aPartId, perm);
if (aPartId == "indexedDB") {
initIndexedDBRow();
}
}
function onCheckboxClick(aPartId)
@ -148,6 +160,9 @@ function onCheckboxClick(aPartId)
var checkbox = document.getElementById(aPartId + "Def");
if (checkbox.checked) {
permissionManager.remove(gPermURI.host, aPartId);
if (aPartId == "indexedDB") {
permissionManager.remove(gPermURI.host, "indexedDB-unlimited");
}
command.setAttribute("disabled", "true");
var perm = gPermObj[aPartId]();
setRadioState(aPartId, perm);
@ -167,6 +182,9 @@ function onRadioClick(aPartId)
var id = radioGroup.selectedItem.id;
var permission = id.split('#')[1];
permissionManager.add(gPermURI, aPartId, permission);
if (aPartId == "indexedDB" && permission == BLOCK) {
permissionManager.remove(gPermURI.host, "indexedDB-unlimited");
}
}
function setRadioState(aPartId, aValue)
@ -174,3 +192,41 @@ function setRadioState(aPartId, aValue)
var radio = document.getElementById(aPartId + "#" + aValue);
radio.radioGroup.selectedItem = radio;
}
function initIndexedDBRow()
{
var status = document.getElementById("indexedDBStatus");
var button = document.getElementById("indexedDBClear");
var usage = Components.classes["@mozilla.org/dom/indexeddb/manager;1"]
.getService(nsIIndexedDatabaseManager)
.getUsageForURI(gPermURI);
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()
{
Components.classes["@mozilla.org/dom/indexeddb/manager;1"]
.getService(nsIIndexedDatabaseManager)
.clearDatabasesForURI(gPermURI);
var permissionManager = Components.classes[PERMISSION_CONTRACTID]
.getService(nsIPermissionManager);
permissionManager.remove(gPermURI.host, "indexedDB");
permissionManager.remove(gPermURI.host, "indexedDB-unlimited");
initIndexedDBRow();
}

Просмотреть файл

@ -99,6 +99,10 @@
<!ENTITY permInstall "Install Extensions or Themes">
<!ENTITY permGeo "Share Location">
<!ENTITY permIndexedDB "Maintain Offline Storage">
<!ENTITY permClearStorage "Clear Storage">
<!ENTITY permClearStorage.accesskey "C">
<!ENTITY securityTab "Security">
<!ENTITY securityTab.accesskey "S">
<!ENTITY securityHeader "Security information for this page">

Просмотреть файл

@ -79,3 +79,10 @@ feedXML=XML
securityNoOwner=This web site does not supply ownership information.
securityOneVisit=Yes, once
securityNVisits=Yes, %S times
# LOCALIZATION NOTE: The next string is for the disk usage of the
# database
# e.g. indexedDBUsage : "50.23 MB"
# %1$S = size (in bytes or megabytes, ...)
# %2$S = unit of measure (bytes, KB, MB, ...)
indexedDBUsage=This web site is using %1$S %2$S

Просмотреть файл

@ -179,8 +179,6 @@ AsyncConnectionHelper::Run()
}
}
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "GetOrCreateConnection failed!");
if (connection) {
rv = connection->SetProgressHandler(kProgressHandlerGranularity, this,
getter_AddRefs(mOldProgressHandler));
@ -200,7 +198,11 @@ AsyncConnectionHelper::Run()
}
}
else {
mErrorCode = nsIIDBDatabaseException::UNKNOWN_ERR;
// NS_ERROR_NOT_AVAILABLE is our special code for "database is invalidated"
// and we should fail with RECOVERABLE_ERR.
mErrorCode = rv == NS_ERROR_NOT_AVAILABLE ?
nsIIDBDatabaseException::RECOVERABLE_ERR :
nsIIDBDatabaseException::UNKNOWN_ERR;
}
if (!mStartTime.IsNull()) {
@ -226,6 +228,12 @@ NS_IMETHODIMP
AsyncConnectionHelper::OnProgress(mozIStorageConnection* aConnection,
PRBool* _retval)
{
if (mDatabase && mDatabase->IsInvalidated()) {
// Someone is trying to delete the database file. Exit lightningfast!
*_retval = PR_TRUE;
return NS_OK;
}
TimeDuration elapsed = TimeStamp::Now() - mStartTime;
if (elapsed >= mTimeoutDuration) {
*_retval = PR_TRUE;

Просмотреть файл

@ -54,7 +54,9 @@
#include "IDBObjectStore.h"
#include "IDBTransaction.h"
#include "IDBFactory.h"
#include "IndexedDatabaseManager.h"
#include "LazyIdleThread.h"
#include "TransactionThreadPool.h"
USING_INDEXEDDB_NAMESPACE
@ -248,12 +250,23 @@ IDBDatabase::Create(nsIScriptContext* aScriptContext,
db->mConnection.swap(aConnection);
IndexedDatabaseManager* mgr = IndexedDatabaseManager::GetInstance();
NS_ASSERTION(mgr, "This should never be null!");
if (!mgr->RegisterDatabase(db)) {
NS_WARNING("Out of memory?");
return nsnull;
}
return db.forget();
}
IDBDatabase::IDBDatabase()
: mDatabaseId(0)
: mDatabaseId(0),
mInvalidated(0)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (!gDatabaseInstanceCount++) {
NS_ASSERTION(!gPromptHelpersMutex, "Should be null!");
gPromptHelpersMutex = new mozilla::Mutex("IDBDatabase gPromptHelpersMutex");
@ -262,6 +275,12 @@ IDBDatabase::IDBDatabase()
IDBDatabase::~IDBDatabase()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
IndexedDatabaseManager* mgr = IndexedDatabaseManager::GetInstance();
if (mgr) {
mgr->UnregisterDatabase(this);
}
if (mConnectionThread) {
mConnectionThread->SetWeakIdleObserver(nsnull);
}
@ -300,6 +319,10 @@ IDBDatabase::GetOrCreateConnection(mozIStorageConnection** aResult)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
if (mInvalidated) {
return NS_ERROR_NOT_AVAILABLE;
}
if (!mConnection) {
mConnection = IDBFactory::GetConnection(mFilePath);
NS_ENSURE_TRUE(mConnection, NS_ERROR_FAILURE);
@ -369,6 +392,30 @@ IDBDatabase::IsQuotaDisabled()
return foundHelper->PromptAndReturnQuotaIsDisabled();
}
void
IDBDatabase::Invalidate()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
PR_AtomicSet(&mInvalidated, 1);
CloseConnection();
}
bool
IDBDatabase::IsInvalidated()
{
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_TRAVERSE_BEGIN_INHERITED(IDBDatabase,

Просмотреть файл

@ -119,6 +119,11 @@ public:
return mASCIIOrigin;
}
void Invalidate();
bool IsInvalidated();
void WaitForConnectionReleased();
private:
IDBDatabase();
~IDBDatabase();
@ -131,6 +136,7 @@ private:
nsString mDescription;
nsString mFilePath;
nsCString mASCIIOrigin;
PRInt32 mInvalidated;
nsRefPtr<LazyIdleThread> mConnectionThread;

Просмотреть файл

@ -61,6 +61,7 @@
#include "DatabaseInfo.h"
#include "IDBDatabase.h"
#include "IDBKeyRange.h"
#include "IndexedDatabaseManager.h"
#include "LazyIdleThread.h"
#define PREF_INDEXEDDB_QUOTA "dom.indexedDB.warningQuota"
@ -623,6 +624,29 @@ IDBFactory::GetIndexedDBQuota()
return PRUint32(PR_MAX(gIndexedDBQuota, 0));
}
// static
nsresult
IDBFactory::GetDirectoryForOrigin(const nsACString& aASCIIOrigin,
nsIFile** aDirectory)
{
nsCOMPtr<nsIFile> directory;
nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
getter_AddRefs(directory));
NS_ENSURE_SUCCESS(rv, rv);
rv = directory->Append(NS_LITERAL_STRING("indexedDB"));
NS_ENSURE_SUCCESS(rv, rv);
NS_ConvertASCIItoUTF16 originSanitized(aASCIIOrigin);
originSanitized.ReplaceChar(":/", '+');
rv = directory->Append(originSanitized);
NS_ENSURE_SUCCESS(rv, rv);
directory.forget(aDirectory);
return NS_OK;
}
NS_IMPL_ADDREF(IDBFactory)
NS_IMPL_RELEASE(IDBFactory)
@ -703,7 +727,12 @@ IDBFactory::Open(const nsAString& aName,
nsRefPtr<CheckPermissionsHelper> permissionHelper =
new CheckPermissionsHelper(openHelper, thread, innerWindow, origin);
rv = NS_DispatchToCurrentThread(permissionHelper);
nsRefPtr<IndexedDatabaseManager> mgr =
already_AddRefed<IndexedDatabaseManager>(
IndexedDatabaseManager::GetOrCreateInstance());
NS_ENSURE_TRUE(mgr, NS_ERROR_FAILURE);
rv = mgr->WaitForClearAndDispatch(origin, permissionHelper);
NS_ENSURE_SUCCESS(rv, rv);
request.forget(_retval);

Просмотреть файл

@ -66,6 +66,10 @@ public:
static PRUint32
GetIndexedDBQuota();
static nsresult
GetDirectoryForOrigin(const nsACString& aASCIIOrigin,
nsIFile** aDirectory);
private:
IDBFactory() { }
~IDBFactory() { }

Просмотреть файл

@ -242,6 +242,10 @@ IDBTransaction::GetOrCreateConnection(mozIStorageConnection** aResult)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
if (mDatabase->IsInvalidated()) {
return NS_ERROR_NOT_AVAILABLE;
}
if (!mConnection) {
nsCOMPtr<mozIStorageConnection> connection =
IDBFactory::GetConnection(mDatabase->FilePath());
@ -765,7 +769,12 @@ CommitHelper::Run()
return NS_OK;
}
IDBFactory::SetCurrentDatabase(mTransaction->Database());
IDBDatabase* database = mTransaction->Database();
if (database->IsInvalidated()) {
mAborted = true;
}
IDBFactory::SetCurrentDatabase(database);
if (mAborted) {
NS_ASSERTION(mConnection, "This had better not be null!");

Просмотреть файл

@ -0,0 +1,420 @@
/* -*- 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 "IndexedDatabaseManager.h"
#include "nsIFile.h"
#include "nsIObserverService.h"
#include "nsISimpleEnumerator.h"
#include "mozilla/Services.h"
#include "nsContentUtils.h"
#include "nsThreadUtils.h"
#include "nsXPCOM.h"
#include "IDBDatabase.h"
#include "IDBFactory.h"
USING_INDEXEDDB_NAMESPACE
using namespace mozilla::services;
namespace {
bool gShutdown = false;
// Holds a reference!
IndexedDatabaseManager* gInstance = nsnull;
// Adds all databases in the hash to the given array.
PLDHashOperator
EnumerateToTArray(const nsACString& aKey,
nsTArray<IDBDatabase*>* aValue,
void* aUserArg)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
NS_ASSERTION(aValue, "Null pointer!");
NS_ASSERTION(aUserArg, "Null pointer!");
nsTArray<nsRefPtr<IDBDatabase> >* array =
static_cast<nsTArray<nsRefPtr<IDBDatabase> >* >(aUserArg);
if (!array->AppendElements(*aValue)) {
NS_WARNING("Out of memory!");
return PL_DHASH_STOP;
}
return PL_DHASH_NEXT;
}
} // anonymous namespace
IndexedDatabaseManager::IndexedDatabaseManager()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!gInstance, "More than one instance!");
}
IndexedDatabaseManager::~IndexedDatabaseManager()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(gInstance == this, "Different instances!");
gInstance = nsnull;
}
// Returns a raw pointer that carries an owning reference! Lame, but the
// singleton factory macros force this.
IndexedDatabaseManager*
IndexedDatabaseManager::GetOrCreateInstance()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (gShutdown) {
NS_ERROR("Calling GetOrCreateInstance() after shutdown!");
return nsnull;
}
if (!gInstance) {
nsRefPtr<IndexedDatabaseManager> instance(new IndexedDatabaseManager());
if (!instance->mLiveDatabases.Init()) {
NS_WARNING("Out of memory!");
return nsnull;
}
// We need to know when to release the singleton.
nsCOMPtr<nsIObserverService> obs = GetObserverService();
nsresult rv = obs->AddObserver(instance, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
PR_FALSE);
NS_ENSURE_SUCCESS(rv, nsnull);
instance.forget(&gInstance);
}
NS_IF_ADDREF(gInstance);
return gInstance;
}
// Does not return an owning reference.
IndexedDatabaseManager*
IndexedDatabaseManager::GetInstance()
{
return gInstance;
}
// Called when an IDBDatabase is constructed.
bool
IndexedDatabaseManager::RegisterDatabase(IDBDatabase* aDatabase)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aDatabase, "Null pointer!");
// Don't allow any new databases to be created after shutdown.
if (gShutdown) {
return false;
}
// Add this database to its origin array if it exists, create it otherwise.
nsTArray<IDBDatabase*>* array;
if (!mLiveDatabases.Get(aDatabase->Origin(), &array)) {
nsAutoPtr<nsTArray<IDBDatabase*> > newArray(new nsTArray<IDBDatabase*>());
if (!mLiveDatabases.Put(aDatabase->Origin(), newArray)) {
NS_WARNING("Out of memory?");
return false;
}
array = newArray.forget();
}
if (!array->AppendElement(aDatabase)) {
NS_WARNING("Out of memory?");
return false;
}
return true;
}
// Called when an IDBDatabase is destroyed.
void
IndexedDatabaseManager::UnregisterDatabase(IDBDatabase* aDatabase)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aDatabase, "Null pointer!");
// Remove this database from its origin array, maybe remove the array if it
// is then empty.
nsTArray<IDBDatabase*>* array;
if (mLiveDatabases.Get(aDatabase->Origin(), &array) &&
array->RemoveElement(aDatabase)) {
if (array->IsEmpty()) {
mLiveDatabases.Remove(aDatabase->Origin());
}
return;
}
NS_ERROR("Didn't know anything about this database!");
}
nsresult
IndexedDatabaseManager::WaitForClearAndDispatch(const nsACString& aOrigin,
nsIRunnable* aRunnable)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!aOrigin.IsEmpty(), "Empty origin!");
NS_ASSERTION(aRunnable, "Null pointer!");
// 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.
PRUint32 count = mOriginClearData.Length();
for (PRUint32 index = 0; index < count; index++) {
OriginClearData& data = mOriginClearData[index];
if (data.origin == aOrigin) {
nsCOMPtr<nsIRunnable>* newPtr =
data.delayedRunnables.AppendElement(aRunnable);
NS_ENSURE_TRUE(newPtr, NS_ERROR_OUT_OF_MEMORY);
return NS_OK;
}
}
// We aren't currently clearing databases for this origin, so dispatch the
// runnable immediately.
return NS_DispatchToCurrentThread(aRunnable);
}
NS_IMPL_ISUPPORTS2(IndexedDatabaseManager, nsIIndexedDatabaseManager,
nsIObserver)
NS_IMETHODIMP
IndexedDatabaseManager::GetUsageForURI(nsIURI* aURI,
PRUint64* _retval)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ENSURE_ARG_POINTER(aURI);
// Figure out which origin we're dealing with.
nsCString origin;
nsresult rv = nsContentUtils::GetASCIIOrigin(aURI, origin);
NS_ENSURE_SUCCESS(rv, rv);
// Non-standard URIs can't create databases anyway, so return 0.
if (origin.EqualsLiteral("null")) {
*_retval = 0;
return NS_OK;
}
// Get the directory where we may be storing database files for this origin.
nsCOMPtr<nsIFile> directory;
rv = IDBFactory::GetDirectoryForOrigin(origin, getter_AddRefs(directory));
NS_ENSURE_SUCCESS(rv, rv);
PRBool exists;
rv = directory->Exists(&exists);
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;
return NS_OK;
}
NS_IMETHODIMP
IndexedDatabaseManager::ClearDatabasesForURI(nsIURI* aURI)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ENSURE_ARG_POINTER(aURI);
// Figure out which origin we're dealing with.
nsCString origin;
nsresult rv = nsContentUtils::GetASCIIOrigin(aURI, origin);
NS_ENSURE_SUCCESS(rv, rv);
// Non-standard URIs can't create databases anyway, so return immediately.
if (origin.EqualsLiteral("null")) {
return NS_OK;
}
// If we're already clearing out files for this origin then return
// immediately.
PRUint32 clearDataCount = mOriginClearData.Length();
for (PRUint32 index = 0; index < clearDataCount; index++) {
if (mOriginClearData[index].origin == origin) {
return NS_OK;
}
}
// We need to grab references to any live databases here to prevent them from
// dying while we invalidate them.
nsTArray<nsRefPtr<IDBDatabase> > liveDatabases;
// Grab all live databases for this origin.
nsTArray<IDBDatabase*>* array;
if (mLiveDatabases.Get(origin, &array)) {
if (!liveDatabases.AppendElements(*array)) {
NS_WARNING("Out of memory?");
return NS_ERROR_OUT_OF_MEMORY;
}
}
// Make a new entry for this origin in mOriginClearData. Don't return early
// while mOriginClearData has this origin in it!
OriginClearData* data = mOriginClearData.AppendElement();
NS_ENSURE_TRUE(data, NS_ERROR_OUT_OF_MEMORY);
data->origin = origin;
if (!liveDatabases.IsEmpty()) {
PRUint32 count = liveDatabases.Length();
// Invalidate all the live databases first.
for (PRUint32 index = 0; index < count; index++) {
liveDatabases[index]->Invalidate();
}
// Now wait for them to finish.
for (PRUint32 index = 0; index < count; index++) {
liveDatabases[index]->WaitForConnectionReleased();
}
}
// Now that all our databases have released their connections to their files
// we can go ahead and delete the directory. Don't return early while
// 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;
}
NS_IMETHODIMP
IndexedDatabaseManager::Observe(nsISupports* aSubject,
const char* aTopic,
const PRUnichar* aData)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
// Setting this flag prevents the servic from being recreated and prevents
// further databases from being created.
gShutdown = true;
// Grab all live databases, for all origins. Need references to keep them
// alive while we wait on them.
nsAutoTArray<nsRefPtr<IDBDatabase>, 50> liveDatabases;
mLiveDatabases.EnumerateRead(EnumerateToTArray, &liveDatabases);
// Wait for all live databases to finish.
if (!liveDatabases.IsEmpty()) {
PRUint32 count = liveDatabases.Length();
for (PRUint32 index = 0; index < count; index++) {
liveDatabases[index]->WaitForConnectionReleased();
}
}
mLiveDatabases.Clear();
// This may kill us.
gInstance->Release();
return NS_OK;
}
NS_NOTREACHED("Unknown topic!");
return NS_ERROR_UNEXPECTED;
}

Просмотреть файл

@ -0,0 +1,101 @@
/* -*- 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_indexeddatabasemanager_h__
#define mozilla_dom_indexeddb_indexeddatabasemanager_h__
#include "mozilla/dom/indexedDB/IndexedDatabase.h"
#include "nsIIndexedDatabaseManager.h"
#include "nsIObserver.h"
#include "nsClassHashtable.h"
#include "nsHashKeys.h"
#define INDEXEDDB_MANAGER_CONTRACTID \
"@mozilla.org/dom/indexeddb/manager;1"
class nsIRunnable;
BEGIN_INDEXEDDB_NAMESPACE
class IDBDatabase;
class IndexedDatabaseManager : public nsIIndexedDatabaseManager,
public nsIObserver
{
friend class IDBDatabase;
public:
// Returns an owning reference!
static IndexedDatabaseManager* GetOrCreateInstance();
// Returns a non-owning reference.
static IndexedDatabaseManager* GetInstance();
NS_DECL_ISUPPORTS
NS_DECL_NSIINDEXEDDATABASEMANAGER
NS_DECL_NSIOBSERVER
nsresult WaitForClearAndDispatch(const nsACString& aOrigin,
nsIRunnable* aRunnable);
private:
IndexedDatabaseManager();
~IndexedDatabaseManager();
bool RegisterDatabase(IDBDatabase* aDatabase);
void UnregisterDatabase(IDBDatabase* aDatabase);
struct OriginClearData
{
nsCString origin;
nsTArray<nsCOMPtr<nsIRunnable> > delayedRunnables;
};
// Maintains a list of live databases per origin.
nsClassHashtable<nsCStringHashKey, nsTArray<IDBDatabase*> > mLiveDatabases;
// Maintains a list of origins that are currently being cleared.
nsAutoTArray<OriginClearData, 1> mOriginClearData;
};
END_INDEXEDDB_NAMESPACE
#endif /* mozilla_dom_indexeddb_indexeddatabasemanager_h__ */

Просмотреть файл

@ -65,6 +65,7 @@ CPPSRCS = \
IDBRequest.cpp \
IDBTransaction.cpp \
IDBFactory.cpp \
IndexedDatabaseManager.cpp \
LazyIdleThread.cpp \
TransactionThreadPool.cpp \
$(NULL)
@ -79,6 +80,7 @@ EXPORTS_mozilla/dom/indexedDB = \
IDBRequest.h \
IDBTransaction.h \
IndexedDatabase.h \
IndexedDatabaseManager.h \
IDBFactory.h \
LazyIdleThread.h \
$(NULL)
@ -108,6 +110,7 @@ XPIDLSRCS = \
nsIIDBTransaction.idl \
nsIIDBTransactionEvent.idl \
nsIIDBFactory.idl \
nsIIndexedDatabaseManager.idl \
$(NULL)
ifdef ENABLE_TESTS

Просмотреть файл

@ -143,6 +143,13 @@ TransactionThreadPool::GetOrCreate()
return gInstance;
}
// static
TransactionThreadPool*
TransactionThreadPool::Get()
{
return gInstance;
}
// static
void
TransactionThreadPool::Shutdown()
@ -384,6 +391,10 @@ TransactionThreadPool::Dispatch(IDBTransaction* aTransaction,
NS_ASSERTION(aTransaction, "Null pointer!");
NS_ASSERTION(aRunnable, "Null pointer!");
if (aTransaction->mDatabase->IsInvalidated() && !aFinish) {
return NS_ERROR_NOT_AVAILABLE;
}
bool canRun;
TransactionQueue* existingQueue;
nsresult rv = TransactionCanRun(aTransaction, &canRun, &existingQueue);
@ -472,6 +483,24 @@ TransactionThreadPool::Dispatch(IDBTransaction* aTransaction,
return mThreadPool->Dispatch(transactionInfo->queue, NS_DISPATCH_NORMAL);
}
void
TransactionThreadPool::WaitForAllTransactionsToComplete(IDBDatabase* aDatabase)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
const PRUint32 databaseId = aDatabase->Id();
nsIThread* currentThread = NS_GetCurrentThread();
// As soon as all of the transactions for this database are complete its
// entry in mTransactionsInProgress will be removed, so just loop while
// checking.
while (mTransactionsInProgress.Get(databaseId, nsnull)) {
if (NS_FAILED(NS_ProcessNextEvent(currentThread, PR_TRUE))) {
NS_WARNING("Failed to process next event?!");
}
}
}
NS_IMPL_THREADSAFE_ISUPPORTS1(TransactionThreadPool, nsIObserver)
NS_IMETHODIMP

Просмотреть файл

@ -71,6 +71,7 @@ public:
// returns a non-owning ref!
static TransactionThreadPool* GetOrCreate();
static TransactionThreadPool* Get();
static void Shutdown();
nsresult Dispatch(IDBTransaction* aTransaction,
@ -78,6 +79,8 @@ public:
bool aFinish,
nsIRunnable* aFinishRunnable);
void WaitForAllTransactionsToComplete(IDBDatabase* aDatabase);
protected:
class TransactionQueue : public nsIRunnable
{

Просмотреть файл

@ -0,0 +1,50 @@
/* -*- 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 "nsISupports.idl"
interface nsIURI;
[scriptable, uuid(dba13d2e-ea4f-4011-9b66-a8b0e61a7f84)]
interface nsIIndexedDatabaseManager : nsISupports
{
unsigned long long getUsageForURI(in nsIURI aURI);
void clearDatabasesForURI(in nsIURI aURI);
};

Просмотреть файл

@ -242,6 +242,10 @@
#define NS_ICONTENTUTILS_CID \
{ 0x762C4AE7, 0xB923, 0x422F, { 0xB9, 0x7E, 0xB9, 0xBF, 0xC1, 0xEF, 0x7B, 0xF0 } }
// {1A26A7B7-D06E-4F45-8B45-D7AD60F7A9AB}
#define INDEXEDDB_MANAGER_CID \
{ 0x1a26a7b7, 0xd06e, 0x4f45, { 0x8b, 0x45, 0xd7, 0xad, 0x60, 0xf7, 0xa9, 0xab } }
#ifdef MOZ_MEDIA
#define NS_HTMLAUDIOELEMENT_CID \
{ /* 1d40026b-4c44-4f6f-b158-26bb5e9c65e9 */ \

Просмотреть файл

@ -133,6 +133,9 @@
#include "nsDOMScriptObjectFactory.h"
#include "nsDOMStorage.h"
#include "nsJSON.h"
#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
using mozilla::dom::indexedDB::IndexedDatabaseManager;
// Editor stuff
#include "nsEditorCID.h"
@ -321,6 +324,8 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsDOMParser)
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsDOMStorageManager,
nsDOMStorageManager::GetInstance)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsChannelPolicy)
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(IndexedDatabaseManager,
IndexedDatabaseManager::GetOrCreateInstance)
#if defined(XP_UNIX) || \
defined(_WINDOWS) || \
defined(machintosh) || \
@ -853,6 +858,7 @@ NS_DEFINE_NAMED_CID(NS_DOMSTORAGE2_CID);
NS_DEFINE_NAMED_CID(NS_DOMSTORAGEMANAGER_CID);
NS_DEFINE_NAMED_CID(NS_DOMJSON_CID);
NS_DEFINE_NAMED_CID(NS_TEXTEDITOR_CID);
NS_DEFINE_NAMED_CID(INDEXEDDB_MANAGER_CID );
#ifndef MOZILLA_PLAINTEXT_EDITOR_ONLY
#ifdef ENABLE_EDITOR_API_LOG
NS_DEFINE_NAMED_CID(NS_HTMLEDITOR_CID);
@ -1003,6 +1009,7 @@ static const mozilla::Module::CIDEntry kLayoutCIDs[] = {
{ &kNS_DOMSTORAGEMANAGER_CID, false, NULL, nsDOMStorageManagerConstructor },
{ &kNS_DOMJSON_CID, false, NULL, NS_NewJSON },
{ &kNS_TEXTEDITOR_CID, false, NULL, nsPlaintextEditorConstructor },
{ &kINDEXEDDB_MANAGER_CID, false, NULL, IndexedDatabaseManagerConstructor },
#ifndef MOZILLA_PLAINTEXT_EDITOR_ONLY
#ifdef ENABLE_EDITOR_API_LOG
{ &kNS_HTMLEDITOR_CID, false, NULL, nsHTMLEditorLogConstructor },
@ -1147,6 +1154,7 @@ static const mozilla::Module::ContractIDEntry kLayoutContracts[] = {
{ "@mozilla.org/dom/storagemanager;1", &kNS_DOMSTORAGEMANAGER_CID },
{ "@mozilla.org/dom/json;1", &kNS_DOMJSON_CID },
{ "@mozilla.org/editor/texteditor;1", &kNS_TEXTEDITOR_CID },
{ INDEXEDDB_MANAGER_CONTRACTID, &kINDEXEDDB_MANAGER_CID },
#ifndef MOZILLA_PLAINTEXT_EDITOR_ONLY
#ifdef ENABLE_EDITOR_API_LOG
{ "@mozilla.org/editor/htmleditor;1", &kNS_HTMLEDITOR_CID },