summaryrefslogtreecommitdiff
path: root/Source/WebCore/Modules/webdatabase/DatabaseTracker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/Modules/webdatabase/DatabaseTracker.cpp')
-rw-r--r--Source/WebCore/Modules/webdatabase/DatabaseTracker.cpp918
1 files changed, 426 insertions, 492 deletions
diff --git a/Source/WebCore/Modules/webdatabase/DatabaseTracker.cpp b/Source/WebCore/Modules/webdatabase/DatabaseTracker.cpp
index 8d3fa13a0..437097717 100644
--- a/Source/WebCore/Modules/webdatabase/DatabaseTracker.cpp
+++ b/Source/WebCore/Modules/webdatabase/DatabaseTracker.cpp
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -29,27 +29,26 @@
#include "config.h"
#include "DatabaseTracker.h"
-#if ENABLE(SQL_DATABASE)
-
-#include "Chrome.h"
-#include "ChromeClient.h"
#include "Database.h"
-#include "DatabaseBackendBase.h"
-#include "DatabaseBackendContext.h"
+#include "DatabaseContext.h"
#include "DatabaseManager.h"
#include "DatabaseManagerClient.h"
#include "DatabaseThread.h"
+#include "ExceptionCode.h"
#include "FileSystem.h"
#include "Logging.h"
#include "OriginLock.h"
-#include "Page.h"
#include "SecurityOrigin.h"
+#include "SecurityOriginData.h"
#include "SecurityOriginHash.h"
#include "SQLiteFileSystem.h"
#include "SQLiteStatement.h"
+#include "UUID.h"
#include <wtf/MainThread.h>
+#include <wtf/NeverDestroyed.h>
#include <wtf/StdLibExtras.h>
#include <wtf/text/CString.h>
+#include <wtf/text/StringBuilder.h>
#if PLATFORM(IOS)
#include "WebCoreThread.h"
@@ -57,41 +56,40 @@
namespace WebCore {
-static DatabaseTracker* staticTracker = 0;
+static Vector<String> isolatedCopy(const Vector<String>& original)
+{
+ Vector<String> copy;
+ copy.reserveInitialCapacity(original.size());
+ for (auto& string : original)
+ copy.uncheckedAppend(string.isolatedCopy());
+ return copy;
+}
+
+std::unique_ptr<DatabaseTracker> DatabaseTracker::trackerWithDatabasePath(const String& databasePath)
+{
+ return std::unique_ptr<DatabaseTracker>(new DatabaseTracker(databasePath));
+}
+
+static DatabaseTracker* staticTracker = nullptr;
void DatabaseTracker::initializeTracker(const String& databasePath)
{
ASSERT(!staticTracker);
if (staticTracker)
return;
-
staticTracker = new DatabaseTracker(databasePath);
}
-DatabaseTracker& DatabaseTracker::tracker()
+DatabaseTracker& DatabaseTracker::singleton()
{
if (!staticTracker)
- staticTracker = new DatabaseTracker("");
-
+ staticTracker = new DatabaseTracker(emptyString());
return *staticTracker;
}
DatabaseTracker::DatabaseTracker(const String& databasePath)
- : m_client(0)
+ : m_databaseDirectoryPath(databasePath.isolatedCopy())
{
- setDatabaseDirectoryPath(databasePath);
-}
-
-void DatabaseTracker::setDatabaseDirectoryPath(const String& path)
-{
- MutexLocker lockDatabase(m_databaseGuard);
- ASSERT(!m_database.isOpen());
- m_databaseDirectoryPath = path.isolatedCopy();
-}
-
-String DatabaseTracker::databaseDirectoryPath() const
-{
- return m_databaseDirectoryPath.isolatedCopy();
}
String DatabaseTracker::trackerDatabasePath() const
@@ -118,7 +116,7 @@ void DatabaseTracker::openTrackerDatabase(TrackerCreationAction createAction)
if (!m_database.open(databasePath)) {
// FIXME: What do do here?
- LOG_ERROR("Failed to open databasePath %s.", databasePath.ascii().data());
+ LOG_ERROR("Failed to open databasePath %s.", databasePath.utf8().data());
return;
}
m_database.disableThreadingChecks();
@@ -138,47 +136,41 @@ void DatabaseTracker::openTrackerDatabase(TrackerCreationAction createAction)
}
}
-bool DatabaseTracker::hasAdequateQuotaForOrigin(SecurityOrigin* origin, unsigned long estimatedSize, DatabaseError& err)
+ExceptionOr<void> DatabaseTracker::hasAdequateQuotaForOrigin(const SecurityOriginData& origin, unsigned estimatedSize)
{
ASSERT(!m_databaseGuard.tryLock());
- unsigned long long usage = usageForOrigin(origin);
+ auto usage = this->usage(origin);
// If the database will fit, allow its creation.
- unsigned long long requirement = usage + std::max<unsigned long long>(1, estimatedSize);
+ auto requirement = usage + std::max<unsigned long long>(1, estimatedSize);
if (requirement < usage) {
// The estimated size is so big it causes an overflow; don't allow creation.
- err = DatabaseError::DatabaseSizeOverflowed;
- return false;
+ return Exception { SECURITY_ERR };
}
- if (requirement <= quotaForOriginNoLock(origin))
- return true;
-
- err = DatabaseError::DatabaseSizeExceededQuota;
- return false;
+ if (requirement > quotaNoLock(origin))
+ return Exception { QUOTA_EXCEEDED_ERR };
+ return { };
}
-bool DatabaseTracker::canEstablishDatabase(DatabaseBackendContext* context, const String& name, unsigned long estimatedSize, DatabaseError& error)
+ExceptionOr<void> DatabaseTracker::canEstablishDatabase(DatabaseContext& context, const String& name, unsigned estimatedSize)
{
- error = DatabaseError::None;
+ LockHolder lockDatabase(m_databaseGuard);
- MutexLocker lockDatabase(m_databaseGuard);
- SecurityOrigin* origin = context->securityOrigin();
+ // FIXME: What guarantees this context.securityOrigin() is non-null?
+ auto origin = context.securityOrigin();
- if (isDeletingDatabaseOrOriginFor(origin, name)) {
- error = DatabaseError::DatabaseIsBeingDeleted;
- return false;
- }
+ if (isDeletingDatabaseOrOriginFor(origin, name))
+ return Exception { SECURITY_ERR };
recordCreatingDatabase(origin, name);
// If a database already exists, ignore the passed-in estimated size and say it's OK.
if (hasEntryForDatabase(origin, name))
- return true;
+ return { };
- if (hasAdequateQuotaForOrigin(origin, estimatedSize, error)) {
- ASSERT(error == DatabaseError::None);
- return true;
- }
+ auto result = hasAdequateQuotaForOrigin(origin, estimatedSize);
+ if (!result.hasException())
+ return { };
// If we get here, then we do not have enough quota for one of the
// following reasons as indicated by the set error:
@@ -193,12 +185,11 @@ bool DatabaseTracker::canEstablishDatabase(DatabaseBackendContext* context, cons
// a chance to update the quota and call retryCanEstablishDatabase() to try
// again. Hence, we don't call doneCreatingDatabase() yet in that case.
- if (error == DatabaseError::DatabaseSizeOverflowed)
+ auto exception = result.releaseException();
+ if (exception.code() != QUOTA_EXCEEDED_ERR)
doneCreatingDatabase(origin, name);
- else
- ASSERT(error == DatabaseError::DatabaseSizeExceededQuota);
- return false;
+ return WTFMove(exception);
}
// Note: a thought about performance: hasAdequateQuotaForOrigin() was also
@@ -209,30 +200,30 @@ bool DatabaseTracker::canEstablishDatabase(DatabaseBackendContext* context, cons
// hasAdequateQuotaForOrigin() simple and correct (i.e. bug free), and just
// re-use it. Also note that the path for opening a database involves IO, and
// hence should not be a performance critical path anyway.
-bool DatabaseTracker::retryCanEstablishDatabase(DatabaseBackendContext* context, const String& name, unsigned long estimatedSize, DatabaseError& error)
+ExceptionOr<void> DatabaseTracker::retryCanEstablishDatabase(DatabaseContext& context, const String& name, unsigned estimatedSize)
{
- error = DatabaseError::None;
+ LockHolder lockDatabase(m_databaseGuard);
- MutexLocker lockDatabase(m_databaseGuard);
- SecurityOrigin* origin = context->securityOrigin();
+ // FIXME: What guarantees context.securityOrigin() is non-null?
+ auto origin = context.securityOrigin();
// We have already eliminated other types of errors in canEstablishDatabase().
// The only reason we're in retryCanEstablishDatabase() is because we gave
// the client a chance to update the quota and are rechecking it here.
// If we fail this check, the only possible reason this time should be due
// to inadequate quota.
- if (hasAdequateQuotaForOrigin(origin, estimatedSize, error)) {
- ASSERT(error == DatabaseError::None);
- return true;
- }
+ auto result = hasAdequateQuotaForOrigin(origin, estimatedSize);
+ if (!result.hasException())
+ return { };
- ASSERT(error == DatabaseError::DatabaseSizeExceededQuota);
+ auto exception = result.releaseException();
+ ASSERT(exception.code() == QUOTA_EXCEEDED_ERR);
doneCreatingDatabase(origin, name);
- return false;
+ return WTFMove(exception);
}
-bool DatabaseTracker::hasEntryForOriginNoLock(SecurityOrigin* origin)
+bool DatabaseTracker::hasEntryForOriginNoLock(const SecurityOriginData& origin)
{
ASSERT(!m_databaseGuard.tryLock());
openTrackerDatabase(DontCreateIfDoesNotExist);
@@ -240,23 +231,17 @@ bool DatabaseTracker::hasEntryForOriginNoLock(SecurityOrigin* origin)
return false;
SQLiteStatement statement(m_database, "SELECT origin FROM Origins where origin=?;");
- if (statement.prepare() != SQLResultOk) {
+ if (statement.prepare() != SQLITE_OK) {
LOG_ERROR("Failed to prepare statement.");
return false;
}
- statement.bindText(1, origin->databaseIdentifier());
-
- return statement.step() == SQLResultRow;
-}
+ statement.bindText(1, origin.databaseIdentifier());
-bool DatabaseTracker::hasEntryForOrigin(SecurityOrigin* origin)
-{
- MutexLocker lockDatabase(m_databaseGuard);
- return hasEntryForOriginNoLock(origin);
+ return statement.step() == SQLITE_ROW;
}
-bool DatabaseTracker::hasEntryForDatabase(SecurityOrigin* origin, const String& databaseIdentifier)
+bool DatabaseTracker::hasEntryForDatabase(const SecurityOriginData& origin, const String& databaseIdentifier)
{
ASSERT(!m_databaseGuard.tryLock());
openTrackerDatabase(DontCreateIfDoesNotExist);
@@ -268,25 +253,25 @@ bool DatabaseTracker::hasEntryForDatabase(SecurityOrigin* origin, const String&
// We've got a tracker database. Set up a query to ask for the db of interest:
SQLiteStatement statement(m_database, "SELECT guid FROM Databases WHERE origin=? AND name=?;");
- if (statement.prepare() != SQLResultOk)
+ if (statement.prepare() != SQLITE_OK)
return false;
- statement.bindText(1, origin->databaseIdentifier());
+ statement.bindText(1, origin.databaseIdentifier());
statement.bindText(2, databaseIdentifier);
- return statement.step() == SQLResultRow;
+ return statement.step() == SQLITE_ROW;
}
-unsigned long long DatabaseTracker::getMaxSizeForDatabase(const DatabaseBackendBase* database)
+unsigned long long DatabaseTracker::maximumSize(Database& database)
{
// The maximum size for a database is the full quota for its origin, minus the current usage within the origin,
// plus the current usage of the given database
- MutexLocker lockDatabase(m_databaseGuard);
- SecurityOrigin* origin = database->securityOrigin();
+ LockHolder lockDatabase(m_databaseGuard);
+ auto origin = database.securityOrigin();
- unsigned long long quota = quotaForOriginNoLock(origin);
- unsigned long long diskUsage = usageForOrigin(origin);
- unsigned long long databaseFileSize = SQLiteFileSystem::getDatabaseFileSize(database->fileName());
+ unsigned long long quota = quotaNoLock(origin);
+ unsigned long long diskUsage = usage(origin);
+ unsigned long long databaseFileSize = SQLiteFileSystem::getDatabaseFileSize(database.fileName());
ASSERT(databaseFileSize <= diskUsage);
if (diskUsage > quota)
@@ -302,45 +287,47 @@ unsigned long long DatabaseTracker::getMaxSizeForDatabase(const DatabaseBackendB
return maxSize;
}
-void DatabaseTracker::interruptAllDatabasesForContext(const DatabaseBackendContext* context)
+void DatabaseTracker::closeAllDatabases(CurrentQueryBehavior currentQueryBehavior)
{
- Vector<RefPtr<DatabaseBackendBase>> openDatabases;
+ Vector<Ref<Database>> openDatabases;
{
- MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
-
+ LockHolder openDatabaseMapLock(m_openDatabaseMapGuard);
if (!m_openDatabaseMap)
return;
-
- DatabaseNameMap* nameMap = m_openDatabaseMap->get(context->securityOrigin());
- if (!nameMap)
- return;
-
- DatabaseNameMap::const_iterator dbNameMapEndIt = nameMap->end();
- for (DatabaseNameMap::const_iterator dbNameMapIt = nameMap->begin(); dbNameMapIt != dbNameMapEndIt; ++dbNameMapIt) {
- DatabaseSet* databaseSet = dbNameMapIt->value;
- DatabaseSet::const_iterator dbSetEndIt = databaseSet->end();
- for (DatabaseSet::const_iterator dbSetIt = databaseSet->begin(); dbSetIt != dbSetEndIt; ++dbSetIt) {
- if ((*dbSetIt)->databaseContext() == context)
- openDatabases.append(*dbSetIt);
+ for (auto& nameMap : m_openDatabaseMap->values()) {
+ for (auto& set : nameMap->values()) {
+ for (auto& database : *set)
+ openDatabases.append(*database);
}
}
}
+ for (auto& database : openDatabases) {
+ if (currentQueryBehavior == CurrentQueryBehavior::Interrupt)
+ database->interrupt();
+ database->close();
+ }
+}
- Vector<RefPtr<DatabaseBackendBase>>::const_iterator openDatabasesEndIt = openDatabases.end();
- for (Vector<RefPtr<DatabaseBackendBase>>::const_iterator openDatabasesIt = openDatabases.begin(); openDatabasesIt != openDatabasesEndIt; ++openDatabasesIt)
- (*openDatabasesIt)->interrupt();
+String DatabaseTracker::originPath(const SecurityOriginData& origin) const
+{
+ return SQLiteFileSystem::appendDatabaseFileNameToPath(m_databaseDirectoryPath.isolatedCopy(), origin.databaseIdentifier());
}
-String DatabaseTracker::originPath(SecurityOrigin* origin) const
+static String generateDatabaseFileName()
{
- return SQLiteFileSystem::appendDatabaseFileNameToPath(m_databaseDirectoryPath.isolatedCopy(), origin->databaseIdentifier());
+ StringBuilder stringBuilder;
+
+ stringBuilder.append(createCanonicalUUIDString());
+ stringBuilder.appendLiteral(".db");
+
+ return stringBuilder.toString();
}
-String DatabaseTracker::fullPathForDatabaseNoLock(SecurityOrigin* origin, const String& name, bool createIfNotExists)
+String DatabaseTracker::fullPathForDatabaseNoLock(const SecurityOriginData& origin, const String& name, bool createIfNotExists)
{
ASSERT(!m_databaseGuard.tryLock());
- String originIdentifier = origin->databaseIdentifier();
+ String originIdentifier = origin.databaseIdentifier();
String originPath = this->originPath(origin);
// Make sure the path for this SecurityOrigin exists
@@ -352,7 +339,7 @@ String DatabaseTracker::fullPathForDatabaseNoLock(SecurityOrigin* origin, const
return String();
SQLiteStatement statement(m_database, "SELECT path FROM Databases WHERE origin=? AND name=?;");
- if (statement.prepare() != SQLResultOk)
+ if (statement.prepare() != SQLITE_OK)
return String();
statement.bindText(1, originIdentifier);
@@ -360,18 +347,19 @@ String DatabaseTracker::fullPathForDatabaseNoLock(SecurityOrigin* origin, const
int result = statement.step();
- if (result == SQLResultRow)
+ if (result == SQLITE_ROW)
return SQLiteFileSystem::appendDatabaseFileNameToPath(originPath, statement.getColumnText(0));
if (!createIfNotExists)
return String();
- if (result != SQLResultDone) {
- LOG_ERROR("Failed to retrieve filename from Database Tracker for origin %s, name %s", originIdentifier.ascii().data(), name.ascii().data());
+ if (result != SQLITE_DONE) {
+ LOG_ERROR("Failed to retrieve filename from Database Tracker for origin %s, name %s", originIdentifier.utf8().data(), name.utf8().data());
return String();
}
statement.finalize();
- String fileName = SQLiteFileSystem::getFileNameForNewDatabase(originPath, name, originIdentifier, &m_database);
+ String fileName = generateDatabaseFileName();
+
if (!addDatabase(origin, name, fileName))
return String();
@@ -382,102 +370,100 @@ String DatabaseTracker::fullPathForDatabaseNoLock(SecurityOrigin* origin, const
return fullFilePath;
}
-String DatabaseTracker::fullPathForDatabase(SecurityOrigin* origin, const String& name, bool createIfNotExists)
+String DatabaseTracker::fullPathForDatabase(const SecurityOriginData& origin, const String& name, bool createIfNotExists)
{
- MutexLocker lockDatabase(m_databaseGuard);
+ LockHolder lockDatabase(m_databaseGuard);
return fullPathForDatabaseNoLock(origin, name, createIfNotExists).isolatedCopy();
}
-void DatabaseTracker::origins(Vector<RefPtr<SecurityOrigin>>& originsResult)
+Vector<SecurityOriginData> DatabaseTracker::origins()
{
- MutexLocker lockDatabase(m_databaseGuard);
+ LockHolder lockDatabase(m_databaseGuard);
openTrackerDatabase(DontCreateIfDoesNotExist);
if (!m_database.isOpen())
- return;
+ return { };
SQLiteStatement statement(m_database, "SELECT origin FROM Origins");
- if (statement.prepare() != SQLResultOk) {
+ if (statement.prepare() != SQLITE_OK) {
LOG_ERROR("Failed to prepare statement.");
- return;
+ return { };
}
- int result;
- while ((result = statement.step()) == SQLResultRow) {
- RefPtr<SecurityOrigin> origin = SecurityOrigin::createFromDatabaseIdentifier(statement.getColumnText(0));
- originsResult.append(origin->isolatedCopy());
- }
- originsResult.shrinkToFit();
+ Vector<SecurityOriginData> origins;
+ int stepResult;
+ while ((stepResult = statement.step()) == SQLITE_ROW)
+ origins.append(SecurityOriginData::fromDatabaseIdentifier(statement.getColumnText(0))->isolatedCopy());
+ origins.shrinkToFit();
- if (result != SQLResultDone)
+ if (stepResult != SQLITE_DONE)
LOG_ERROR("Failed to read in all origins from the database.");
+
+ return origins;
}
-bool DatabaseTracker::databaseNamesForOriginNoLock(SecurityOrigin* origin, Vector<String>& resultVector)
+Vector<String> DatabaseTracker::databaseNamesNoLock(const SecurityOriginData& origin)
{
ASSERT(!m_databaseGuard.tryLock());
openTrackerDatabase(DontCreateIfDoesNotExist);
if (!m_database.isOpen())
- return false;
+ return { };
SQLiteStatement statement(m_database, "SELECT name FROM Databases where origin=?;");
+ if (statement.prepare() != SQLITE_OK)
+ return { };
- if (statement.prepare() != SQLResultOk)
- return false;
-
- statement.bindText(1, origin->databaseIdentifier());
+ statement.bindText(1, origin.databaseIdentifier());
+ Vector<String> names;
int result;
- while ((result = statement.step()) == SQLResultRow)
- resultVector.append(statement.getColumnText(0));
+ while ((result = statement.step()) == SQLITE_ROW)
+ names.append(statement.getColumnText(0));
+ names.shrinkToFit();
- if (result != SQLResultDone) {
- LOG_ERROR("Failed to retrieve all database names for origin %s", origin->databaseIdentifier().ascii().data());
- return false;
+ if (result != SQLITE_DONE) {
+ LOG_ERROR("Failed to retrieve all database names for origin %s", origin.databaseIdentifier().utf8().data());
+ return { };
}
- return true;
+ return names;
}
-bool DatabaseTracker::databaseNamesForOrigin(SecurityOrigin* origin, Vector<String>& resultVector)
+Vector<String> DatabaseTracker::databaseNames(const SecurityOriginData& origin)
{
- Vector<String> temp;
+ Vector<String> names;
{
- MutexLocker lockDatabase(m_databaseGuard);
- if (!databaseNamesForOriginNoLock(origin, temp))
- return false;
+ LockHolder lockDatabase(m_databaseGuard);
+ names = databaseNamesNoLock(origin);
}
-
- for (Vector<String>::iterator iter = temp.begin(); iter != temp.end(); ++iter)
- resultVector.append(iter->isolatedCopy());
- return true;
+ return isolatedCopy(names);
}
-DatabaseDetails DatabaseTracker::detailsForNameAndOrigin(const String& name, SecurityOrigin* origin)
+DatabaseDetails DatabaseTracker::detailsForNameAndOrigin(const String& name, const SecurityOriginData& origin)
{
- String originIdentifier = origin->databaseIdentifier();
+ String originIdentifier = origin.databaseIdentifier();
String displayName;
int64_t expectedUsage;
{
- MutexLocker lockDatabase(m_databaseGuard);
+ LockHolder lockDatabase(m_databaseGuard);
openTrackerDatabase(DontCreateIfDoesNotExist);
if (!m_database.isOpen())
return DatabaseDetails();
SQLiteStatement statement(m_database, "SELECT displayName, estimatedSize FROM Databases WHERE origin=? AND name=?");
- if (statement.prepare() != SQLResultOk)
+ if (statement.prepare() != SQLITE_OK)
return DatabaseDetails();
statement.bindText(1, originIdentifier);
statement.bindText(2, name);
int result = statement.step();
- if (result == SQLResultDone)
+ if (result == SQLITE_DONE)
return DatabaseDetails();
- if (result != SQLResultRow) {
- LOG_ERROR("Error retrieving details for database %s in origin %s from tracker database", name.ascii().data(), originIdentifier.ascii().data());
+ if (result != SQLITE_ROW) {
+ LOG_ERROR("Error retrieving details for database %s in origin %s from tracker database", name.utf8().data(), originIdentifier.utf8().data());
return DatabaseDetails();
}
displayName = statement.getColumnText(0);
@@ -490,51 +476,50 @@ DatabaseDetails DatabaseTracker::detailsForNameAndOrigin(const String& name, Sec
return DatabaseDetails(name, displayName, expectedUsage, SQLiteFileSystem::getDatabaseFileSize(path), SQLiteFileSystem::databaseCreationTime(path), SQLiteFileSystem::databaseModificationTime(path));
}
-void DatabaseTracker::setDatabaseDetails(SecurityOrigin* origin, const String& name, const String& displayName, unsigned long estimatedSize)
+void DatabaseTracker::setDatabaseDetails(const SecurityOriginData& origin, const String& name, const String& displayName, unsigned estimatedSize)
{
- String originIdentifier = origin->databaseIdentifier();
+ String originIdentifier = origin.databaseIdentifier();
int64_t guid = 0;
- MutexLocker lockDatabase(m_databaseGuard);
+ LockHolder lockDatabase(m_databaseGuard);
openTrackerDatabase(CreateIfDoesNotExist);
if (!m_database.isOpen())
return;
SQLiteStatement statement(m_database, "SELECT guid FROM Databases WHERE origin=? AND name=?");
- if (statement.prepare() != SQLResultOk)
+ if (statement.prepare() != SQLITE_OK)
return;
statement.bindText(1, originIdentifier);
statement.bindText(2, name);
int result = statement.step();
- if (result == SQLResultRow)
+ if (result == SQLITE_ROW)
guid = statement.getColumnInt64(0);
statement.finalize();
if (guid == 0) {
- if (result != SQLResultDone)
- LOG_ERROR("Error to determing existence of database %s in origin %s in tracker database", name.ascii().data(), originIdentifier.ascii().data());
+ if (result != SQLITE_DONE)
+ LOG_ERROR("Error to determing existence of database %s in origin %s in tracker database", name.utf8().data(), originIdentifier.utf8().data());
else {
// This case should never occur - we should never be setting database details for a database that doesn't already exist in the tracker
// But since the tracker file is an external resource not under complete control of our code, it's somewhat invalid to make this an ASSERT case
// So we'll print an error instead
- LOG_ERROR("Could not retrieve guid for database %s in origin %s from the tracker database - it is invalid to set database details on a database that doesn't already exist in the tracker",
- name.ascii().data(), originIdentifier.ascii().data());
+ LOG_ERROR("Could not retrieve guid for database %s in origin %s from the tracker database - it is invalid to set database details on a database that doesn't already exist in the tracker", name.utf8().data(), originIdentifier.utf8().data());
}
return;
}
SQLiteStatement updateStatement(m_database, "UPDATE Databases SET displayName=?, estimatedSize=? WHERE guid=?");
- if (updateStatement.prepare() != SQLResultOk)
+ if (updateStatement.prepare() != SQLITE_OK)
return;
updateStatement.bindText(1, displayName);
updateStatement.bindInt64(2, estimatedSize);
updateStatement.bindInt64(3, guid);
- if (updateStatement.step() != SQLResultDone) {
- LOG_ERROR("Failed to update details for database %s in origin %s", name.ascii().data(), originIdentifier.ascii().data());
+ if (updateStatement.step() != SQLITE_DONE) {
+ LOG_ERROR("Failed to update details for database %s in origin %s", name.utf8().data(), originIdentifier.utf8().data());
return;
}
@@ -542,108 +527,82 @@ void DatabaseTracker::setDatabaseDetails(SecurityOrigin* origin, const String& n
m_client->dispatchDidModifyDatabase(origin, name);
}
-void DatabaseTracker::doneCreatingDatabase(DatabaseBackendBase* database)
+void DatabaseTracker::doneCreatingDatabase(Database& database)
{
- MutexLocker lockDatabase(m_databaseGuard);
- doneCreatingDatabase(database->securityOrigin(), database->stringIdentifier());
+ LockHolder lockDatabase(m_databaseGuard);
+ doneCreatingDatabase(database.securityOrigin(), database.stringIdentifier());
}
-void DatabaseTracker::addOpenDatabase(DatabaseBackendBase* database)
+void DatabaseTracker::addOpenDatabase(Database& database)
{
- if (!database)
- return;
+ LockHolder openDatabaseMapLock(m_openDatabaseMapGuard);
- {
- MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
+ if (!m_openDatabaseMap)
+ m_openDatabaseMap = std::make_unique<DatabaseOriginMap>();
- if (!m_openDatabaseMap)
- m_openDatabaseMap = adoptPtr(new DatabaseOriginMap);
+ auto origin = database.securityOrigin();
- String name(database->stringIdentifier());
- DatabaseNameMap* nameMap = m_openDatabaseMap->get(database->securityOrigin());
- if (!nameMap) {
- nameMap = new DatabaseNameMap;
- m_openDatabaseMap->set(database->securityOrigin()->isolatedCopy(), nameMap);
- }
+ auto* nameMap = m_openDatabaseMap->get(origin);
+ if (!nameMap) {
+ nameMap = new DatabaseNameMap;
+ m_openDatabaseMap->add(origin.isolatedCopy(), nameMap);
+ }
- DatabaseSet* databaseSet = nameMap->get(name);
- if (!databaseSet) {
- databaseSet = new DatabaseSet;
- nameMap->set(name.isolatedCopy(), databaseSet);
- }
+ String name = database.stringIdentifier();
+ auto* databaseSet = nameMap->get(name);
+ if (!databaseSet) {
+ databaseSet = new DatabaseSet;
+ nameMap->set(name.isolatedCopy(), databaseSet);
+ }
- databaseSet->add(database);
+ databaseSet->add(&database);
- LOG(StorageAPI, "Added open Database %s (%p)\n", database->stringIdentifier().ascii().data(), database);
- }
+ LOG(StorageAPI, "Added open Database %s (%p)\n", database.stringIdentifier().utf8().data(), &database);
}
-void DatabaseTracker::removeOpenDatabase(DatabaseBackendBase* database)
+void DatabaseTracker::removeOpenDatabase(Database& database)
{
- if (!database)
- return;
+ LockHolder openDatabaseMapLock(m_openDatabaseMapGuard);
- {
- MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
-
- if (!m_openDatabaseMap) {
- ASSERT_NOT_REACHED();
- return;
- }
-
- String name(database->stringIdentifier());
- DatabaseNameMap* nameMap = m_openDatabaseMap->get(database->securityOrigin());
- if (!nameMap) {
- ASSERT_NOT_REACHED();
- return;
- }
-
- DatabaseSet* databaseSet = nameMap->get(name);
- if (!databaseSet) {
- ASSERT_NOT_REACHED();
- return;
- }
-
- databaseSet->remove(database);
-
- LOG(StorageAPI, "Removed open Database %s (%p)\n", database->stringIdentifier().ascii().data(), database);
+ if (!m_openDatabaseMap) {
+ ASSERT_NOT_REACHED();
+ return;
+ }
- if (!databaseSet->isEmpty())
- return;
+ DatabaseNameMap* nameMap = m_openDatabaseMap->get(database.securityOrigin());
+ if (!nameMap) {
+ ASSERT_NOT_REACHED();
+ return;
+ }
- nameMap->remove(name);
- delete databaseSet;
+ String name = database.stringIdentifier();
+ auto* databaseSet = nameMap->get(name);
+ if (!databaseSet) {
+ ASSERT_NOT_REACHED();
+ return;
+ }
- if (!nameMap->isEmpty())
- return;
+ databaseSet->remove(&database);
- m_openDatabaseMap->remove(database->securityOrigin());
- delete nameMap;
- }
-}
+ LOG(StorageAPI, "Removed open Database %s (%p)\n", database.stringIdentifier().utf8().data(), &database);
-void DatabaseTracker::getOpenDatabases(SecurityOrigin* origin, const String& name, HashSet<RefPtr<DatabaseBackendBase>>* databases)
-{
- MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
- if (!m_openDatabaseMap)
+ if (!databaseSet->isEmpty())
return;
- DatabaseNameMap* nameMap = m_openDatabaseMap->get(origin);
- if (!nameMap)
- return;
+ nameMap->remove(name);
+ delete databaseSet;
- DatabaseSet* databaseSet = nameMap->get(name);
- if (!databaseSet)
+ if (!nameMap->isEmpty())
return;
- for (DatabaseSet::iterator it = databaseSet->begin(); it != databaseSet->end(); ++it)
- databases->add(*it);
+ m_openDatabaseMap->remove(database.securityOrigin());
+ delete nameMap;
}
-PassRefPtr<OriginLock> DatabaseTracker::originLockFor(SecurityOrigin* origin)
+RefPtr<OriginLock> DatabaseTracker::originLockFor(const SecurityOriginData& origin)
{
- MutexLocker lockDatabase(m_databaseGuard);
- String databaseIdentifier = origin->databaseIdentifier();
+ LockHolder lockDatabase(m_databaseGuard);
+ String databaseIdentifier = origin.databaseIdentifier();
// The originLockMap is accessed from multiple DatabaseThreads since
// different script contexts can be writing to different databases from
@@ -658,14 +617,14 @@ PassRefPtr<OriginLock> DatabaseTracker::originLockFor(SecurityOrigin* origin)
return addResult.iterator->value;
String path = originPath(origin);
- RefPtr<OriginLock> lock = adoptRef(new OriginLock(path));
+ RefPtr<OriginLock> lock = adoptRef(*new OriginLock(path));
ASSERT(lock);
addResult.iterator->value = lock;
- return lock.release();
+ return lock;
}
-void DatabaseTracker::deleteOriginLockFor(SecurityOrigin* origin)
+void DatabaseTracker::deleteOriginLockFor(const SecurityOriginData& origin)
{
ASSERT(!m_databaseGuard.tryLock());
@@ -680,29 +639,25 @@ void DatabaseTracker::deleteOriginLockFor(SecurityOrigin* origin)
// files in this origin. We'll give the OriginLock one chance to do an
// orderly clean up first when we remove its ref from the m_originLockMap.
// This may or may not be possible depending on whether other threads are
- // also using the OriginLock at the same time. After that, we will go ahead
- // and delete the lock file.
+ // also using the OriginLock at the same time. After that, we will delete the lock file.
- m_originLockMap.remove(origin->databaseIdentifier());
+ m_originLockMap.remove(origin.databaseIdentifier());
OriginLock::deleteLockFile(originPath(origin));
}
-unsigned long long DatabaseTracker::usageForOrigin(SecurityOrigin* origin)
+unsigned long long DatabaseTracker::usage(const SecurityOriginData& origin)
{
String originPath = this->originPath(origin);
unsigned long long diskUsage = 0;
- Vector<String> fileNames = listDirectory(originPath, String("*.db"));
- Vector<String>::iterator fileName = fileNames.begin();
- Vector<String>::iterator lastFileName = fileNames.end();
- for (; fileName != lastFileName; ++fileName) {
+ for (auto& fileName : listDirectory(originPath, ASCIILiteral("*.db"))) {
long long size;
- getFileSize(*fileName, size);
+ getFileSize(fileName, size);
diskUsage += size;
}
return diskUsage;
}
-unsigned long long DatabaseTracker::quotaForOriginNoLock(SecurityOrigin* origin)
+unsigned long long DatabaseTracker::quotaNoLock(const SecurityOriginData& origin)
{
ASSERT(!m_databaseGuard.tryLock());
unsigned long long quota = 0;
@@ -712,82 +667,73 @@ unsigned long long DatabaseTracker::quotaForOriginNoLock(SecurityOrigin* origin)
return quota;
SQLiteStatement statement(m_database, "SELECT quota FROM Origins where origin=?;");
- if (statement.prepare() != SQLResultOk) {
+ if (statement.prepare() != SQLITE_OK) {
LOG_ERROR("Failed to prepare statement.");
return quota;
}
- statement.bindText(1, origin->databaseIdentifier());
+ statement.bindText(1, origin.databaseIdentifier());
- if (statement.step() == SQLResultRow)
+ if (statement.step() == SQLITE_ROW)
quota = statement.getColumnInt64(0);
return quota;
}
-unsigned long long DatabaseTracker::quotaForOrigin(SecurityOrigin* origin)
+unsigned long long DatabaseTracker::quota(const SecurityOriginData& origin)
{
- MutexLocker lockDatabase(m_databaseGuard);
- return quotaForOriginNoLock(origin);
+ LockHolder lockDatabase(m_databaseGuard);
+ return quotaNoLock(origin);
}
-void DatabaseTracker::setQuota(SecurityOrigin* origin, unsigned long long quota)
+void DatabaseTracker::setQuota(const SecurityOriginData& origin, unsigned long long quota)
{
- MutexLocker lockDatabase(m_databaseGuard);
+ LockHolder lockDatabase(m_databaseGuard);
- if (quotaForOriginNoLock(origin) == quota)
+ if (quotaNoLock(origin) == quota)
return;
openTrackerDatabase(CreateIfDoesNotExist);
if (!m_database.isOpen())
return;
-#if PLATFORM(IOS)
bool insertedNewOrigin = false;
-#endif
bool originEntryExists = hasEntryForOriginNoLock(origin);
if (!originEntryExists) {
SQLiteStatement statement(m_database, "INSERT INTO Origins VALUES (?, ?)");
- if (statement.prepare() != SQLResultOk) {
- LOG_ERROR("Unable to establish origin %s in the tracker", origin->databaseIdentifier().ascii().data());
+ if (statement.prepare() != SQLITE_OK) {
+ LOG_ERROR("Unable to establish origin %s in the tracker", origin.databaseIdentifier().utf8().data());
} else {
- statement.bindText(1, origin->databaseIdentifier());
+ statement.bindText(1, origin.databaseIdentifier());
statement.bindInt64(2, quota);
- if (statement.step() != SQLResultDone)
- LOG_ERROR("Unable to establish origin %s in the tracker", origin->databaseIdentifier().ascii().data());
-#if PLATFORM(IOS)
+ if (statement.step() != SQLITE_DONE)
+ LOG_ERROR("Unable to establish origin %s in the tracker", origin.databaseIdentifier().utf8().data());
else
insertedNewOrigin = true;
-#endif
}
} else {
SQLiteStatement statement(m_database, "UPDATE Origins SET quota=? WHERE origin=?");
- bool error = statement.prepare() != SQLResultOk;
+ bool error = statement.prepare() != SQLITE_OK;
if (!error) {
statement.bindInt64(1, quota);
- statement.bindText(2, origin->databaseIdentifier());
+ statement.bindText(2, origin.databaseIdentifier());
error = !statement.executeCommand();
}
if (error)
- LOG_ERROR("Failed to set quota %llu in tracker database for origin %s", quota, origin->databaseIdentifier().ascii().data());
+ LOG_ERROR("Failed to set quota %llu in tracker database for origin %s", quota, origin.databaseIdentifier().utf8().data());
}
- if (m_client)
-#if PLATFORM(IOS)
- {
+ if (m_client) {
if (insertedNewOrigin)
- m_client->dispatchDidAddNewOrigin(origin);
-#endif
+ m_client->dispatchDidAddNewOrigin();
m_client->dispatchDidModifyOrigin(origin);
-#if PLATFORM(IOS)
}
-#endif
}
-bool DatabaseTracker::addDatabase(SecurityOrigin* origin, const String& name, const String& path)
+bool DatabaseTracker::addDatabase(const SecurityOriginData& origin, const String& name, const String& path)
{
ASSERT(!m_databaseGuard.tryLock());
openTrackerDatabase(CreateIfDoesNotExist);
@@ -799,15 +745,15 @@ bool DatabaseTracker::addDatabase(SecurityOrigin* origin, const String& name, co
SQLiteStatement statement(m_database, "INSERT INTO Databases (origin, name, path) VALUES (?, ?, ?);");
- if (statement.prepare() != SQLResultOk)
+ if (statement.prepare() != SQLITE_OK)
return false;
- statement.bindText(1, origin->databaseIdentifier());
+ statement.bindText(1, origin.databaseIdentifier());
statement.bindText(2, name);
statement.bindText(3, path);
if (!statement.executeCommand()) {
- LOG_ERROR("Failed to add database %s to origin %s: %s\n", name.ascii().data(), origin->databaseIdentifier().ascii().data(), m_database.lastErrorMsg());
+ LOG_ERROR("Failed to add database %s to origin %s: %s\n", name.utf8().data(), origin.databaseIdentifier().utf8().data(), m_database.lastErrorMsg());
return false;
}
@@ -817,32 +763,67 @@ bool DatabaseTracker::addDatabase(SecurityOrigin* origin, const String& name, co
return true;
}
-void DatabaseTracker::deleteAllDatabases()
+void DatabaseTracker::deleteAllDatabasesImmediately()
{
- Vector<RefPtr<SecurityOrigin>> originsCopy;
- origins(originsCopy);
+ // This method is only intended for use by DumpRenderTree / WebKitTestRunner.
+ // Actually deleting the databases is necessary to reset to a known state before running
+ // each test case, but may be unsafe in deployment use cases (where multiple applications
+ // may be accessing the same databases concurrently).
+ for (auto& origin : origins())
+ deleteOrigin(origin, DeletionMode::Immediate);
+}
+
+void DatabaseTracker::deleteDatabasesModifiedSince(std::chrono::system_clock::time_point time)
+{
+ for (auto& origin : origins()) {
+ Vector<String> databaseNames = this->databaseNames(origin);
+ Vector<String> databaseNamesToDelete;
+ databaseNamesToDelete.reserveInitialCapacity(databaseNames.size());
+ for (const auto& databaseName : databaseNames) {
+ auto fullPath = fullPathForDatabase(origin, databaseName, false);
+
+ time_t modificationTime;
+ if (!getFileModificationTime(fullPath, modificationTime))
+ continue;
+
+ if (modificationTime < std::chrono::system_clock::to_time_t(time))
+ continue;
- for (unsigned i = 0; i < originsCopy.size(); ++i)
- deleteOrigin(originsCopy[i].get());
+ databaseNamesToDelete.uncheckedAppend(databaseName);
+ }
+
+ if (databaseNames.size() == databaseNamesToDelete.size())
+ deleteOrigin(origin);
+ else {
+ for (const auto& databaseName : databaseNamesToDelete)
+ deleteDatabase(origin, databaseName);
+ }
+ }
}
// It is the caller's responsibility to make sure that nobody is trying to create, delete, open, or close databases in this origin while the deletion is
// taking place.
-bool DatabaseTracker::deleteOrigin(SecurityOrigin* origin)
+bool DatabaseTracker::deleteOrigin(const SecurityOriginData& origin)
+{
+ return deleteOrigin(origin, DeletionMode::Default);
+}
+
+bool DatabaseTracker::deleteOrigin(const SecurityOriginData& origin, DeletionMode deletionMode)
{
Vector<String> databaseNames;
{
- MutexLocker lockDatabase(m_databaseGuard);
+ LockHolder lockDatabase(m_databaseGuard);
openTrackerDatabase(DontCreateIfDoesNotExist);
if (!m_database.isOpen())
return false;
- if (!databaseNamesForOriginNoLock(origin, databaseNames)) {
- LOG_ERROR("Unable to retrieve list of database names for origin %s", origin->databaseIdentifier().ascii().data());
+ databaseNames = databaseNamesNoLock(origin);
+ if (databaseNames.isEmpty()) {
+ LOG_ERROR("Unable to retrieve list of database names for origin %s", origin.databaseIdentifier().utf8().data());
return false;
}
if (!canDeleteOrigin(origin)) {
- LOG_ERROR("Tried to delete an origin (%s) while either creating database in it or already deleting it", origin->databaseIdentifier().ascii().data());
+ LOG_ERROR("Tried to delete an origin (%s) while either creating database in it or already deleting it", origin.databaseIdentifier().utf8().data());
ASSERT_NOT_REACHED();
return false;
}
@@ -850,55 +831,54 @@ bool DatabaseTracker::deleteOrigin(SecurityOrigin* origin)
}
// We drop the lock here because holding locks during a call to deleteDatabaseFile will deadlock.
- for (unsigned i = 0; i < databaseNames.size(); ++i) {
- if (!deleteDatabaseFile(origin, databaseNames[i])) {
+ for (auto& name : databaseNames) {
+ if (!deleteDatabaseFile(origin, name, deletionMode)) {
// Even if the file can't be deleted, we want to try and delete the rest, don't return early here.
- LOG_ERROR("Unable to delete file for database %s in origin %s", databaseNames[i].ascii().data(), origin->databaseIdentifier().ascii().data());
+ LOG_ERROR("Unable to delete file for database %s in origin %s", name.utf8().data(), origin.databaseIdentifier().utf8().data());
}
}
{
- MutexLocker lockDatabase(m_databaseGuard);
+ LockHolder lockDatabase(m_databaseGuard);
deleteOriginLockFor(origin);
doneDeletingOrigin(origin);
SQLiteStatement statement(m_database, "DELETE FROM Databases WHERE origin=?");
- if (statement.prepare() != SQLResultOk) {
- LOG_ERROR("Unable to prepare deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data());
+ if (statement.prepare() != SQLITE_OK) {
+ LOG_ERROR("Unable to prepare deletion of databases from origin %s from tracker", origin.databaseIdentifier().utf8().data());
return false;
}
- statement.bindText(1, origin->databaseIdentifier());
+ statement.bindText(1, origin.databaseIdentifier());
if (!statement.executeCommand()) {
- LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data());
+ LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin.databaseIdentifier().utf8().data());
return false;
}
SQLiteStatement originStatement(m_database, "DELETE FROM Origins WHERE origin=?");
- if (originStatement.prepare() != SQLResultOk) {
- LOG_ERROR("Unable to prepare deletion of origin %s from tracker", origin->databaseIdentifier().ascii().data());
+ if (originStatement.prepare() != SQLITE_OK) {
+ LOG_ERROR("Unable to prepare deletion of origin %s from tracker", origin.databaseIdentifier().utf8().data());
return false;
}
- originStatement.bindText(1, origin->databaseIdentifier());
+ originStatement.bindText(1, origin.databaseIdentifier());
if (!originStatement.executeCommand()) {
- LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data());
+ LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin.databaseIdentifier().utf8().data());
return false;
}
SQLiteFileSystem::deleteEmptyDatabaseDirectory(originPath(origin));
- RefPtr<SecurityOrigin> originPossiblyLastReference = origin;
bool isEmpty = true;
openTrackerDatabase(DontCreateIfDoesNotExist);
if (m_database.isOpen()) {
SQLiteStatement statement(m_database, "SELECT origin FROM Origins");
- if (statement.prepare() != SQLResultOk)
+ if (statement.prepare() != SQLITE_OK)
LOG_ERROR("Failed to prepare statement.");
- else if (statement.step() == SQLResultRow)
+ else if (statement.step() == SQLITE_ROW)
isEmpty = false;
}
@@ -912,134 +892,134 @@ bool DatabaseTracker::deleteOrigin(SecurityOrigin* origin)
if (m_client) {
m_client->dispatchDidModifyOrigin(origin);
-#if PLATFORM(IOS)
m_client->dispatchDidDeleteDatabaseOrigin();
-#endif
- for (unsigned i = 0; i < databaseNames.size(); ++i)
- m_client->dispatchDidModifyDatabase(origin, databaseNames[i]);
+ for (auto& name : databaseNames)
+ m_client->dispatchDidModifyDatabase(origin, name);
}
}
return true;
}
-bool DatabaseTracker::isDeletingDatabaseOrOriginFor(SecurityOrigin *origin, const String& name)
+bool DatabaseTracker::isDeletingDatabaseOrOriginFor(const SecurityOriginData& origin, const String& name)
{
ASSERT(!m_databaseGuard.tryLock());
// Can't create a database while someone else is deleting it; there's a risk of leaving untracked database debris on the disk.
return isDeletingDatabase(origin, name) || isDeletingOrigin(origin);
}
-void DatabaseTracker::recordCreatingDatabase(SecurityOrigin *origin, const String& name)
+void DatabaseTracker::recordCreatingDatabase(const SecurityOriginData& origin, const String& name)
{
ASSERT(!m_databaseGuard.tryLock());
- NameCountMap* nameMap = m_beingCreated.get(origin);
- if (!nameMap) {
- nameMap = new NameCountMap();
- m_beingCreated.set(origin->isolatedCopy(), nameMap);
+
+ // We don't use HashMap::ensure here to avoid making an isolated copy of the origin every time.
+ auto* nameSet = m_beingCreated.get(origin);
+ if (!nameSet) {
+ auto ownedSet = std::make_unique<HashCountedSet<String>>();
+ nameSet = ownedSet.get();
+ m_beingCreated.add(origin.isolatedCopy(), WTFMove(ownedSet));
}
- long count = nameMap->get(name);
- nameMap->set(name.isolatedCopy(), count + 1);
+ nameSet->add(name.isolatedCopy());
}
-void DatabaseTracker::doneCreatingDatabase(SecurityOrigin *origin, const String& name)
+void DatabaseTracker::doneCreatingDatabase(const SecurityOriginData& origin, const String& name)
{
ASSERT(!m_databaseGuard.tryLock());
- NameCountMap* nameMap = m_beingCreated.get(origin);
- ASSERT(nameMap);
- if (!nameMap)
+
+ ASSERT(m_beingCreated.contains(origin));
+
+ auto iterator = m_beingCreated.find(origin);
+ if (iterator == m_beingCreated.end())
return;
- long count = nameMap->get(name);
- ASSERT(count > 0);
- if (count <= 1) {
- nameMap->remove(name);
- if (nameMap->isEmpty()) {
- m_beingCreated.remove(origin);
- delete nameMap;
- }
- } else
- nameMap->set(name, count - 1);
+ auto& countedSet = *iterator->value;
+ ASSERT(countedSet.contains(name));
+
+ if (countedSet.remove(name) && countedSet.isEmpty())
+ m_beingCreated.remove(iterator);
}
-bool DatabaseTracker::creatingDatabase(SecurityOrigin *origin, const String& name)
+bool DatabaseTracker::creatingDatabase(const SecurityOriginData& origin, const String& name)
{
ASSERT(!m_databaseGuard.tryLock());
- NameCountMap* nameMap = m_beingCreated.get(origin);
- return nameMap && nameMap->get(name);
+
+ auto iterator = m_beingCreated.find(origin);
+ return iterator != m_beingCreated.end() && iterator->value->contains(name);
}
-bool DatabaseTracker::canDeleteDatabase(SecurityOrigin *origin, const String& name)
+bool DatabaseTracker::canDeleteDatabase(const SecurityOriginData& origin, const String& name)
{
ASSERT(!m_databaseGuard.tryLock());
return !creatingDatabase(origin, name) && !isDeletingDatabase(origin, name);
}
-void DatabaseTracker::recordDeletingDatabase(SecurityOrigin *origin, const String& name)
+void DatabaseTracker::recordDeletingDatabase(const SecurityOriginData& origin, const String& name)
{
ASSERT(!m_databaseGuard.tryLock());
ASSERT(canDeleteDatabase(origin, name));
- NameSet* nameSet = m_beingDeleted.get(origin);
+
+ // We don't use HashMap::ensure here to avoid making an isolated copy of the origin every time.
+ auto* nameSet = m_beingDeleted.get(origin);
if (!nameSet) {
- nameSet = new NameSet();
- m_beingDeleted.set(origin->isolatedCopy(), nameSet);
+ auto ownedSet = std::make_unique<HashSet<String>>();
+ nameSet = ownedSet.get();
+ m_beingDeleted.add(origin.isolatedCopy(), WTFMove(ownedSet));
}
ASSERT(!nameSet->contains(name));
nameSet->add(name.isolatedCopy());
}
-void DatabaseTracker::doneDeletingDatabase(SecurityOrigin *origin, const String& name)
+void DatabaseTracker::doneDeletingDatabase(const SecurityOriginData& origin, const String& name)
{
ASSERT(!m_databaseGuard.tryLock());
- NameSet* nameSet = m_beingDeleted.get(origin);
- ASSERT(nameSet);
- if (!nameSet)
+ ASSERT(m_beingDeleted.contains(origin));
+
+ auto iterator = m_beingDeleted.find(origin);
+ if (iterator == m_beingDeleted.end())
return;
- ASSERT(nameSet->contains(name));
- nameSet->remove(name);
- if (nameSet->isEmpty()) {
- m_beingDeleted.remove(origin);
- delete nameSet;
- }
+ ASSERT(iterator->value->contains(name));
+ iterator->value->remove(name);
+ if (iterator->value->isEmpty())
+ m_beingDeleted.remove(iterator);
}
-bool DatabaseTracker::isDeletingDatabase(SecurityOrigin *origin, const String& name)
+bool DatabaseTracker::isDeletingDatabase(const SecurityOriginData& origin, const String& name)
{
ASSERT(!m_databaseGuard.tryLock());
- NameSet* nameSet = m_beingDeleted.get(origin);
+ auto* nameSet = m_beingDeleted.get(origin);
return nameSet && nameSet->contains(name);
}
-bool DatabaseTracker::canDeleteOrigin(SecurityOrigin *origin)
+bool DatabaseTracker::canDeleteOrigin(const SecurityOriginData& origin)
{
ASSERT(!m_databaseGuard.tryLock());
return !(isDeletingOrigin(origin) || m_beingCreated.get(origin));
}
-bool DatabaseTracker::isDeletingOrigin(SecurityOrigin *origin)
+bool DatabaseTracker::isDeletingOrigin(const SecurityOriginData& origin)
{
ASSERT(!m_databaseGuard.tryLock());
return m_originsBeingDeleted.contains(origin);
}
-void DatabaseTracker::recordDeletingOrigin(SecurityOrigin *origin)
+void DatabaseTracker::recordDeletingOrigin(const SecurityOriginData& origin)
{
ASSERT(!m_databaseGuard.tryLock());
ASSERT(!isDeletingOrigin(origin));
- m_originsBeingDeleted.add(origin->isolatedCopy());
+ m_originsBeingDeleted.add(origin.isolatedCopy());
}
-void DatabaseTracker::doneDeletingOrigin(SecurityOrigin *origin)
+void DatabaseTracker::doneDeletingOrigin(const SecurityOriginData& origin)
{
ASSERT(!m_databaseGuard.tryLock());
ASSERT(isDeletingOrigin(origin));
m_originsBeingDeleted.remove(origin);
}
-bool DatabaseTracker::deleteDatabase(SecurityOrigin* origin, const String& name)
+bool DatabaseTracker::deleteDatabase(const SecurityOriginData& origin, const String& name)
{
{
- MutexLocker lockDatabase(m_databaseGuard);
+ LockHolder lockDatabase(m_databaseGuard);
openTrackerDatabase(DontCreateIfDoesNotExist);
if (!m_database.isOpen())
return false;
@@ -1052,27 +1032,27 @@ bool DatabaseTracker::deleteDatabase(SecurityOrigin* origin, const String& name)
}
// We drop the lock here because holding locks during a call to deleteDatabaseFile will deadlock.
- if (!deleteDatabaseFile(origin, name)) {
- LOG_ERROR("Unable to delete file for database %s in origin %s", name.ascii().data(), origin->databaseIdentifier().ascii().data());
- MutexLocker lockDatabase(m_databaseGuard);
+ if (!deleteDatabaseFile(origin, name, DeletionMode::Default)) {
+ LOG_ERROR("Unable to delete file for database %s in origin %s", name.utf8().data(), origin.databaseIdentifier().utf8().data());
+ LockHolder lockDatabase(m_databaseGuard);
doneDeletingDatabase(origin, name);
return false;
}
- MutexLocker lockDatabase(m_databaseGuard);
+ LockHolder lockDatabase(m_databaseGuard);
SQLiteStatement statement(m_database, "DELETE FROM Databases WHERE origin=? AND name=?");
- if (statement.prepare() != SQLResultOk) {
- LOG_ERROR("Unable to prepare deletion of database %s from origin %s from tracker", name.ascii().data(), origin->databaseIdentifier().ascii().data());
+ if (statement.prepare() != SQLITE_OK) {
+ LOG_ERROR("Unable to prepare deletion of database %s from origin %s from tracker", name.utf8().data(), origin.databaseIdentifier().utf8().data());
doneDeletingDatabase(origin, name);
return false;
}
- statement.bindText(1, origin->databaseIdentifier());
+ statement.bindText(1, origin.databaseIdentifier());
statement.bindText(2, name);
if (!statement.executeCommand()) {
- LOG_ERROR("Unable to execute deletion of database %s from origin %s from tracker", name.ascii().data(), origin->databaseIdentifier().ascii().data());
+ LOG_ERROR("Unable to execute deletion of database %s from origin %s from tracker", name.utf8().data(), origin.databaseIdentifier().utf8().data());
doneDeletingDatabase(origin, name);
return false;
}
@@ -1080,9 +1060,7 @@ bool DatabaseTracker::deleteDatabase(SecurityOrigin* origin, const String& name)
if (m_client) {
m_client->dispatchDidModifyOrigin(origin);
m_client->dispatchDidModifyDatabase(origin, name);
-#if PLATFORM(IOS)
m_client->dispatchDidDeleteDatabase();
-#endif
}
doneDeletingDatabase(origin, name);
@@ -1091,7 +1069,7 @@ bool DatabaseTracker::deleteDatabase(SecurityOrigin* origin, const String& name)
// deleteDatabaseFile has to release locks between looking up the list of databases to close and closing them. While this is in progress, the caller
// is responsible for making sure no new databases are opened in the file to be deleted.
-bool DatabaseTracker::deleteDatabaseFile(SecurityOrigin* origin, const String& name)
+bool DatabaseTracker::deleteDatabaseFile(const SecurityOriginData& origin, const String& name, DeletionMode deletionMode)
{
String fullPath = fullPathForDatabase(origin, name, false);
if (fullPath.isEmpty())
@@ -1099,54 +1077,52 @@ bool DatabaseTracker::deleteDatabaseFile(SecurityOrigin* origin, const String& n
#ifndef NDEBUG
{
- MutexLocker lockDatabase(m_databaseGuard);
+ LockHolder lockDatabase(m_databaseGuard);
ASSERT(isDeletingDatabaseOrOriginFor(origin, name));
}
#endif
- Vector<RefPtr<DatabaseBackendBase>> deletedDatabases;
+ Vector<Ref<Database>> deletedDatabases;
// Make sure not to hold the any locks when calling
// Database::markAsDeletedAndClose(), since that can cause a deadlock
// during the synchronous DatabaseThread call it triggers.
{
- MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
+ LockHolder openDatabaseMapLock(m_openDatabaseMapGuard);
if (m_openDatabaseMap) {
- // There are some open databases, lets check if they are for this origin.
- DatabaseNameMap* nameMap = m_openDatabaseMap->get(origin);
- if (nameMap && nameMap->size()) {
- // There are some open databases for this origin, let's check
- // if they are this database by name.
- DatabaseSet* databaseSet = nameMap->get(name);
- if (databaseSet && databaseSet->size()) {
- // We have some database open with this name. Mark them as deleted.
- DatabaseSet::const_iterator end = databaseSet->end();
- for (DatabaseSet::const_iterator it = databaseSet->begin(); it != end; ++it)
- deletedDatabases.append(*it);
+ if (auto* nameMap = m_openDatabaseMap->get(origin)) {
+ if (auto* databaseSet = nameMap->get(name)) {
+ for (auto& database : *databaseSet)
+ deletedDatabases.append(*database);
}
}
}
}
- for (unsigned i = 0; i < deletedDatabases.size(); ++i)
- deletedDatabases[i]->markAsDeletedAndClose();
+ for (auto& database : deletedDatabases)
+ database->markAsDeletedAndClose();
-#if !PLATFORM(IOS)
- return SQLiteFileSystem::deleteDatabaseFile(fullPath);
-#else
- // On the phone, other background processes may still be accessing this database. Deleting the database directly
- // would nuke the POSIX file locks, potentially causing Safari/WebApp to corrupt the new db if it's running in the background.
- // We'll instead truncate the database file to 0 bytes. If another process is operating on this same database file after
- // the truncation, it should get an error since the database file is no longer valid. When Safari is launched
- // next time, it'll go through the database files and clean up any zero-bytes ones.
- SQLiteDatabase database;
- if (database.open(fullPath))
+#if PLATFORM(IOS)
+ if (deletionMode == DeletionMode::Deferred) {
+ // Other background processes may still be accessing this database. Deleting the database directly
+ // would nuke the POSIX file locks, potentially causing Safari/WebApp to corrupt the new db if it's running in the background.
+ // We'll instead truncate the database file to 0 bytes. If another process is operating on this same database file after
+ // the truncation, it should get an error since the database file is no longer valid. When Safari is launched
+ // next time, it'll go through the database files and clean up any zero-bytes ones.
+ SQLiteDatabase database;
+ if (!database.open(fullPath))
+ return false;
return SQLiteFileSystem::truncateDatabaseFile(database.sqlite3Handle());
- return false;
+ }
+#else
+ UNUSED_PARAM(deletionMode);
#endif
+
+ return SQLiteFileSystem::deleteDatabaseFile(fullPath);
}
#if PLATFORM(IOS)
+
void DatabaseTracker::removeDeletedOpenedDatabases()
{
// This is called when another app has deleted a database. Go through all opened databases in this
@@ -1154,7 +1130,7 @@ void DatabaseTracker::removeDeletedOpenedDatabases()
{
// Acquire the lock before calling openTrackerDatabase.
- MutexLocker lockDatabase(m_databaseGuard);
+ LockHolder lockDatabase(m_databaseGuard);
openTrackerDatabase(DontCreateIfDoesNotExist);
}
@@ -1162,43 +1138,36 @@ void DatabaseTracker::removeDeletedOpenedDatabases()
return;
// Keep track of which opened databases have been deleted.
- Vector<RefPtr<Database> > deletedDatabases;
- typedef HashMap<RefPtr<SecurityOrigin>, Vector<String> > DeletedDatabaseMap;
- DeletedDatabaseMap deletedDatabaseMap;
-
+ Vector<RefPtr<Database>> deletedDatabases;
+ Vector<std::pair<SecurityOriginData, Vector<String>>> deletedDatabaseNames;
+
// Make sure not to hold the m_openDatabaseMapGuard mutex when calling
// Database::markAsDeletedAndClose(), since that can cause a deadlock
// during the synchronous DatabaseThread call it triggers.
{
- MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
+ LockHolder openDatabaseMapLock(m_openDatabaseMapGuard);
if (m_openDatabaseMap) {
- DatabaseOriginMap::const_iterator originMapEnd = m_openDatabaseMap->end();
- for (DatabaseOriginMap::const_iterator originMapIt = m_openDatabaseMap->begin(); originMapIt != originMapEnd; ++originMapIt) {
- RefPtr<SecurityOrigin> origin = originMapIt->key;
- DatabaseNameMap* databaseNameMap = originMapIt->value;
+ for (auto& openDatabase : *m_openDatabaseMap) {
+ auto& origin = openDatabase.key;
+ DatabaseNameMap* databaseNameMap = openDatabase.value;
Vector<String> deletedDatabaseNamesForThisOrigin;
// Loop through all opened databases in this origin. Get the current database file path of each database and see if
// it still matches the path stored in the opened database object.
- DatabaseNameMap::const_iterator dbNameMapEnd = databaseNameMap->end();
- for (DatabaseNameMap::const_iterator dbNameMapIt = databaseNameMap->begin(); dbNameMapIt != dbNameMapEnd; ++dbNameMapIt) {
- String databaseName = dbNameMapIt->key;
+ for (auto& databases : *databaseNameMap) {
+ String databaseName = databases.key;
String databaseFileName;
SQLiteStatement statement(m_database, "SELECT path FROM Databases WHERE origin=? AND name=?;");
- if (statement.prepare() == SQLResultOk) {
- statement.bindText(1, origin->databaseIdentifier());
+ if (statement.prepare() == SQLITE_OK) {
+ statement.bindText(1, origin.databaseIdentifier());
statement.bindText(2, databaseName);
- if (statement.step() == SQLResultRow)
+ if (statement.step() == SQLITE_ROW)
databaseFileName = statement.getColumnText(0);
statement.finalize();
}
bool foundDeletedDatabase = false;
- DatabaseSet* databaseSet = dbNameMapIt->value;
- DatabaseSet::const_iterator dbEnd = databaseSet->end();
- for (DatabaseSet::const_iterator dbIt = databaseSet->begin(); dbIt != dbEnd; ++dbIt) {
- Database* db = static_cast<Database*>(*dbIt);
-
+ for (auto& db : *databases.value) {
// We are done if this database has already been marked as deleted.
if (db->deleted())
continue;
@@ -1210,31 +1179,25 @@ void DatabaseTracker::removeDeletedOpenedDatabases()
}
}
- // If the database no longer exists, we should remember to remove it from the OriginQuotaManager later.
- if (foundDeletedDatabase && databaseFileName.isNull())
+ // If the database no longer exists, we should remember to send that information to the client later.
+ if (m_client && foundDeletedDatabase && databaseFileName.isNull())
deletedDatabaseNamesForThisOrigin.append(databaseName);
}
if (!deletedDatabaseNamesForThisOrigin.isEmpty())
- deletedDatabaseMap.set(origin, deletedDatabaseNamesForThisOrigin);
+ deletedDatabaseNames.append({ origin, WTFMove(deletedDatabaseNamesForThisOrigin) });
}
}
}
- for (unsigned i = 0; i < deletedDatabases.size(); ++i)
- deletedDatabases[i]->markAsDeletedAndClose();
-
- DeletedDatabaseMap::const_iterator end = deletedDatabaseMap.end();
- for (DeletedDatabaseMap::const_iterator it = deletedDatabaseMap.begin(); it != end; ++it) {
- SecurityOrigin* origin = it->key.get();
- if (m_client)
- m_client->dispatchDidModifyOrigin(origin);
-
- const Vector<String>& databaseNames = it->value;
- for (unsigned i = 0; i < databaseNames.size(); ++i) {
- if (m_client)
- m_client->dispatchDidModifyDatabase(origin, databaseNames[i]);
- }
+ for (auto& deletedDatabase : deletedDatabases)
+ deletedDatabase->markAsDeletedAndClose();
+
+ for (auto& deletedDatabase : deletedDatabaseNames) {
+ auto& origin = deletedDatabase.first;
+ m_client->dispatchDidModifyOrigin(origin);
+ for (auto& databaseName : deletedDatabase.second)
+ m_client->dispatchDidModifyDatabase(origin, databaseName);
}
}
@@ -1256,20 +1219,20 @@ bool DatabaseTracker::deleteDatabaseFileIfEmpty(const String& path)
// Specify that we want the exclusive locking mode, so after the next read,
// we'll be holding the lock to this database file.
SQLiteStatement lockStatement(database, "PRAGMA locking_mode=EXCLUSIVE;");
- if (lockStatement.prepare() != SQLResultOk)
+ if (lockStatement.prepare() != SQLITE_OK)
return false;
int result = lockStatement.step();
- if (result != SQLResultRow && result != SQLResultDone)
+ if (result != SQLITE_ROW && result != SQLITE_DONE)
return false;
lockStatement.finalize();
// Every sqlite database has a sqlite_master table that contains the schema for the database.
// http://www.sqlite.org/faq.html#q7
SQLiteStatement readStatement(database, "SELECT * FROM sqlite_master LIMIT 1;");
- if (readStatement.prepare() != SQLResultOk)
+ if (readStatement.prepare() != SQLITE_OK)
return false;
// We shouldn't expect any result.
- if (readStatement.step() != SQLResultDone)
+ if (readStatement.step() != SQLITE_DONE)
return false;
readStatement.finalize();
@@ -1281,9 +1244,9 @@ bool DatabaseTracker::deleteDatabaseFileIfEmpty(const String& path)
return SQLiteFileSystem::deleteDatabaseFile(path);
}
-Mutex& DatabaseTracker::openDatabaseMutex()
+Lock& DatabaseTracker::openDatabaseMutex()
{
- DEFINE_STATIC_LOCAL(Mutex, mutex, ());
+ static NeverDestroyed<Lock> mutex;
return mutex;
}
@@ -1299,33 +1262,6 @@ void DatabaseTracker::emptyDatabaseFilesRemovalTaskDidFinish()
openDatabaseMutex().unlock();
}
-void DatabaseTracker::setDatabasesPaused(bool paused)
-{
- MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
- if (!m_openDatabaseMap)
- return;
-
- // This walking is - sadly - the only reliable way to get at each open database thread.
- // This will be cleaner once <rdar://problem/5680441> or some other DB thread consolidation takes place.
- DatabaseOriginMap::iterator i = m_openDatabaseMap.get()->begin();
- DatabaseOriginMap::iterator end = m_openDatabaseMap.get()->end();
-
- for (; i != end; ++i) {
- DatabaseNameMap* databaseNameMap = i->value;
- DatabaseNameMap::iterator j = databaseNameMap->begin();
- DatabaseNameMap::iterator dbNameMapEnd = databaseNameMap->end();
- for (; j != dbNameMapEnd; ++j) {
- DatabaseSet* databaseSet = j->value;
- DatabaseSet::iterator k = databaseSet->begin();
- DatabaseSet::iterator dbSetEnd = databaseSet->end();
- for (; k != dbSetEnd; ++k) {
- DatabaseContext* context = (*k)->databaseContext();
- context->setPaused(paused);
- }
- }
- }
-}
-
#endif
void DatabaseTracker::setClient(DatabaseManagerClient* client)
@@ -1333,25 +1269,24 @@ void DatabaseTracker::setClient(DatabaseManagerClient* client)
m_client = client;
}
-static Mutex& notificationMutex()
+static Lock& notificationMutex()
{
- DEFINE_STATIC_LOCAL(Mutex, mutex, ());
+ static NeverDestroyed<Lock> mutex;
return mutex;
}
-typedef Vector<std::pair<RefPtr<SecurityOrigin>, String>> NotificationQueue;
+using NotificationQueue = Vector<std::pair<SecurityOriginData, String>>;
static NotificationQueue& notificationQueue()
{
- DEFINE_STATIC_LOCAL(NotificationQueue, queue, ());
+ static NeverDestroyed<NotificationQueue> queue;
return queue;
}
-void DatabaseTracker::scheduleNotifyDatabaseChanged(SecurityOrigin* origin, const String& name)
+void DatabaseTracker::scheduleNotifyDatabaseChanged(const SecurityOriginData& origin, const String& name)
{
- MutexLocker locker(notificationMutex());
-
- notificationQueue().append(std::pair<RefPtr<SecurityOrigin>, String>(origin->isolatedCopy(), name.isolatedCopy()));
+ LockHolder locker(notificationMutex());
+ notificationQueue().append(std::make_pair(origin.isolatedCopy(), name.isolatedCopy()));
scheduleForNotification();
}
@@ -1362,33 +1297,32 @@ void DatabaseTracker::scheduleForNotification()
ASSERT(!notificationMutex().tryLock());
if (!notificationScheduled) {
- callOnMainThread(DatabaseTracker::notifyDatabasesChanged, 0);
+ callOnMainThread([] {
+ notifyDatabasesChanged();
+ });
notificationScheduled = true;
}
}
-void DatabaseTracker::notifyDatabasesChanged(void*)
+void DatabaseTracker::notifyDatabasesChanged()
{
// Note that if DatabaseTracker ever becomes non-singleton, we'll have to amend this notification
// mechanism to include which tracker the notification goes out on as well.
- DatabaseTracker& theTracker(tracker());
+ auto& tracker = DatabaseTracker::singleton();
NotificationQueue notifications;
{
- MutexLocker locker(notificationMutex());
-
+ LockHolder locker(notificationMutex());
notifications.swap(notificationQueue());
-
notificationScheduled = false;
}
- if (!theTracker.m_client)
+ if (!tracker.m_client)
return;
- for (unsigned i = 0; i < notifications.size(); ++i)
- theTracker.m_client->dispatchDidModifyDatabase(notifications[i].first.get(), notifications[i].second);
+ for (auto& notification : notifications)
+ tracker.m_client->dispatchDidModifyDatabase(notification.first, notification.second);
}
} // namespace WebCore
-#endif