diff options
Diffstat (limited to 'Source/WebCore/Modules/webdatabase/DatabaseTracker.cpp')
-rw-r--r-- | Source/WebCore/Modules/webdatabase/DatabaseTracker.cpp | 918 |
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 |