summaryrefslogtreecommitdiff
path: root/Source/WebCore/Modules/webdatabase/DatabaseManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/Modules/webdatabase/DatabaseManager.cpp')
-rw-r--r--Source/WebCore/Modules/webdatabase/DatabaseManager.cpp452
1 files changed, 126 insertions, 326 deletions
diff --git a/Source/WebCore/Modules/webdatabase/DatabaseManager.cpp b/Source/WebCore/Modules/webdatabase/DatabaseManager.cpp
index 0b60c96aa..deb8788ef 100644
--- a/Source/WebCore/Modules/webdatabase/DatabaseManager.cpp
+++ b/Source/WebCore/Modules/webdatabase/DatabaseManager.cpp
@@ -20,91 +20,71 @@
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "DatabaseManager.h"
-#if ENABLE(SQL_DATABASE)
-
-#include "AbstractDatabaseServer.h"
#include "Database.h"
-#include "DatabaseBackend.h"
-#include "DatabaseBackendBase.h"
-#include "DatabaseBackendContext.h"
-#include "DatabaseBackendSync.h"
#include "DatabaseCallback.h"
#include "DatabaseContext.h"
-#include "DatabaseStrategy.h"
-#include "DatabaseSync.h"
#include "DatabaseTask.h"
+#include "DatabaseTracker.h"
#include "ExceptionCode.h"
-#include "InspectorDatabaseInstrumentation.h"
+#include "InspectorInstrumentation.h"
#include "Logging.h"
#include "PlatformStrategies.h"
#include "ScriptController.h"
#include "ScriptExecutionContext.h"
#include "SecurityOrigin.h"
+#include "SecurityOriginData.h"
+#include <wtf/NeverDestroyed.h>
namespace WebCore {
-DatabaseManager::ProposedDatabase::ProposedDatabase(DatabaseManager& manager,
- SecurityOrigin* origin, const String& name, const String& displayName, unsigned long estimatedSize)
+class DatabaseManager::ProposedDatabase {
+public:
+ ProposedDatabase(DatabaseManager&, SecurityOrigin&, const String& name, const String& displayName, unsigned long estimatedSize);
+ ~ProposedDatabase();
+
+ SecurityOrigin& origin() { return m_origin; }
+ DatabaseDetails& details() { return m_details; }
+
+private:
+ DatabaseManager& m_manager;
+ Ref<SecurityOrigin> m_origin;
+ DatabaseDetails m_details;
+};
+
+DatabaseManager::ProposedDatabase::ProposedDatabase(DatabaseManager& manager, SecurityOrigin& origin, const String& name, const String& displayName, unsigned long estimatedSize)
: m_manager(manager)
- , m_origin(origin->isolatedCopy())
+ , m_origin(origin.isolatedCopy())
, m_details(name.isolatedCopy(), displayName.isolatedCopy(), estimatedSize, 0, 0, 0)
{
- m_manager.addProposedDatabase(this);
+ m_manager.addProposedDatabase(*this);
}
-DatabaseManager::ProposedDatabase::~ProposedDatabase()
+inline DatabaseManager::ProposedDatabase::~ProposedDatabase()
{
- m_manager.removeProposedDatabase(this);
+ m_manager.removeProposedDatabase(*this);
}
-DatabaseManager& DatabaseManager::manager()
+DatabaseManager& DatabaseManager::singleton()
{
- static DatabaseManager* dbManager = 0;
- // FIXME: The following is vulnerable to a race between threads. Need to
- // implement a thread safe on-first-use static initializer.
- if (!dbManager)
- dbManager = new DatabaseManager();
-
- return *dbManager;
-}
-
-DatabaseManager::DatabaseManager()
- : m_server(platformStrategies()->databaseStrategy()->getDatabaseServer())
- , m_client(0)
- , m_databaseIsAvailable(true)
-#if !ASSERT_DISABLED
- , m_databaseContextRegisteredCount(0)
- , m_databaseContextInstanceCount(0)
-#endif
-{
- ASSERT(m_server); // We should always have a server to work with.
+ static NeverDestroyed<DatabaseManager> instance;
+ return instance;
}
void DatabaseManager::initialize(const String& databasePath)
{
- m_server->initialize(databasePath);
+ DatabaseTracker::initializeTracker(databasePath);
}
void DatabaseManager::setClient(DatabaseManagerClient* client)
{
m_client = client;
- m_server->setClient(client);
-}
-
-String DatabaseManager::databaseDirectoryPath() const
-{
- return m_server->databaseDirectoryPath();
-}
-
-void DatabaseManager::setDatabaseDirectoryPath(const String& path)
-{
- m_server->setDatabaseDirectoryPath(path);
+ DatabaseTracker::singleton().setClient(client);
}
bool DatabaseManager::isAvailable()
@@ -117,359 +97,179 @@ void DatabaseManager::setIsAvailable(bool available)
m_databaseIsAvailable = available;
}
-class DatabaseCreationCallbackTask : public ScriptExecutionContext::Task {
-public:
- static PassOwnPtr<DatabaseCreationCallbackTask> create(PassRefPtr<Database> database, PassRefPtr<DatabaseCallback> creationCallback)
- {
- return adoptPtr(new DatabaseCreationCallbackTask(database, creationCallback));
- }
-
- virtual void performTask(ScriptExecutionContext*)
- {
- m_creationCallback->handleEvent(m_database.get());
- }
-
-private:
- DatabaseCreationCallbackTask(PassRefPtr<Database> database, PassRefPtr<DatabaseCallback> callback)
- : m_database(database)
- , m_creationCallback(callback)
- {
- }
-
- RefPtr<Database> m_database;
- RefPtr<DatabaseCallback> m_creationCallback;
-};
-
-PassRefPtr<DatabaseContext> DatabaseManager::existingDatabaseContextFor(ScriptExecutionContext* context)
+Ref<DatabaseContext> DatabaseManager::databaseContext(ScriptExecutionContext& context)
{
- std::lock_guard<std::mutex> lock(m_mutex);
-
- ASSERT(m_databaseContextRegisteredCount >= 0);
- ASSERT(m_databaseContextInstanceCount >= 0);
- ASSERT(m_databaseContextRegisteredCount <= m_databaseContextInstanceCount);
-
- RefPtr<DatabaseContext> databaseContext = adoptRef(m_contextMap.get(context));
- if (databaseContext) {
- // If we're instantiating a new DatabaseContext, the new instance would
- // carry a new refCount of 1. The client expects this and will simply
- // adoptRef the databaseContext without ref'ing it.
- // However, instead of instantiating a new instance, we're reusing
- // an existing one that corresponds to the specified ScriptExecutionContext.
- // Hence, that new refCount need to be attributed to the reused instance
- // to ensure that the refCount is accurate when the client adopts the ref.
- // We do this by ref'ing the reused databaseContext before returning it.
- databaseContext->ref();
- }
- return databaseContext.release();
+ if (auto databaseContext = context.databaseContext())
+ return *databaseContext;
+ return adoptRef(*new DatabaseContext(context));
}
-PassRefPtr<DatabaseContext> DatabaseManager::databaseContextFor(ScriptExecutionContext* context)
-{
- RefPtr<DatabaseContext> databaseContext = existingDatabaseContextFor(context);
- if (!databaseContext)
- databaseContext = adoptRef(new DatabaseContext(context));
- return databaseContext.release();
-}
+#if LOG_DISABLED
-void DatabaseManager::registerDatabaseContext(DatabaseContext* databaseContext)
+static inline void logOpenDatabaseError(ScriptExecutionContext&, const String&)
{
- std::lock_guard<std::mutex> lock(m_mutex);
-
- ScriptExecutionContext* context = databaseContext->scriptExecutionContext();
- m_contextMap.set(context, databaseContext);
-#if !ASSERT_DISABLED
- m_databaseContextRegisteredCount++;
-#endif
}
-void DatabaseManager::unregisterDatabaseContext(DatabaseContext* databaseContext)
-{
- std::lock_guard<std::mutex> lock(m_mutex);
+#else
- ScriptExecutionContext* context = databaseContext->scriptExecutionContext();
- ASSERT(m_contextMap.get(context));
-#if !ASSERT_DISABLED
- m_databaseContextRegisteredCount--;
-#endif
- m_contextMap.remove(context);
-}
-
-#if !ASSERT_DISABLED
-void DatabaseManager::didConstructDatabaseContext()
+static void logOpenDatabaseError(ScriptExecutionContext& context, const String& name)
{
- std::lock_guard<std::mutex> lock(m_mutex);
-
- m_databaseContextInstanceCount++;
+ LOG(StorageAPI, "Database %s for origin %s not allowed to be established", name.utf8().data(), context.securityOrigin()->toString().utf8().data());
}
-void DatabaseManager::didDestructDatabaseContext()
-{
- std::lock_guard<std::mutex> lock(m_mutex);
-
- m_databaseContextInstanceCount--;
- ASSERT(m_databaseContextRegisteredCount <= m_databaseContextInstanceCount);
-}
#endif
-ExceptionCode DatabaseManager::exceptionCodeForDatabaseError(DatabaseError error)
+ExceptionOr<Ref<Database>> DatabaseManager::openDatabaseBackend(ScriptExecutionContext& context, const String& name, const String& expectedVersion, const String& displayName, unsigned estimatedSize, bool setVersionInNewDatabase)
{
- switch (error) {
- case DatabaseError::None:
- return 0;
- case DatabaseError::DatabaseIsBeingDeleted:
- case DatabaseError::DatabaseSizeExceededQuota:
- case DatabaseError::DatabaseSizeOverflowed:
- case DatabaseError::GenericSecurityError:
- return SECURITY_ERR;
- case DatabaseError::InvalidDatabaseState:
- return INVALID_STATE_ERR;
- }
- ASSERT_NOT_REACHED();
- return 0; // Make some older compilers happy.
-}
-
-static void logOpenDatabaseError(ScriptExecutionContext* context, const String& name)
-{
- UNUSED_PARAM(context);
- UNUSED_PARAM(name);
- LOG(StorageAPI, "Database %s for origin %s not allowed to be established", name.ascii().data(),
- context->securityOrigin()->toString().ascii().data());
-}
-
-PassRefPtr<DatabaseBackendBase> DatabaseManager::openDatabaseBackend(ScriptExecutionContext* context,
- DatabaseType type, const String& name, const String& expectedVersion, const String& displayName,
- unsigned long estimatedSize, bool setVersionInNewDatabase, DatabaseError& error, String& errorMessage)
-{
- ASSERT(error == DatabaseError::None);
+ auto backend = tryToOpenDatabaseBackend(context, name, expectedVersion, displayName, estimatedSize, setVersionInNewDatabase, FirstTryToOpenDatabase);
- RefPtr<DatabaseContext> databaseContext = databaseContextFor(context);
- RefPtr<DatabaseBackendContext> backendContext = databaseContext->backend();
-
- RefPtr<DatabaseBackendBase> backend = m_server->openDatabase(backendContext, type, name, expectedVersion,
- displayName, estimatedSize, setVersionInNewDatabase, error, errorMessage);
-
- if (!backend) {
- ASSERT(error != DatabaseError::None);
-
- switch (error) {
- case DatabaseError::DatabaseIsBeingDeleted:
- case DatabaseError::DatabaseSizeOverflowed:
- case DatabaseError::GenericSecurityError:
- logOpenDatabaseError(context, name);
- return 0;
-
- case DatabaseError::InvalidDatabaseState:
- logErrorMessage(context, errorMessage);
- return 0;
-
- case DatabaseError::DatabaseSizeExceededQuota:
+ if (backend.hasException()) {
+ if (backend.exception().code() == QUOTA_EXCEEDED_ERR) {
// Notify the client that we've exceeded the database quota.
// The client may want to increase the quota, and we'll give it
// one more try after if that is the case.
{
- ProposedDatabase proposedDb(*this, context->securityOrigin(), name, displayName, estimatedSize);
- databaseContext->databaseExceededQuota(name, proposedDb.details());
+ // FIXME: What guarantees context.securityOrigin() is non-null?
+ ProposedDatabase proposedDatabase { *this, *context.securityOrigin(), name, displayName, estimatedSize };
+ this->databaseContext(context)->databaseExceededQuota(name, proposedDatabase.details());
}
- error = DatabaseError::None;
-
- backend = m_server->openDatabase(backendContext, type, name, expectedVersion,
- displayName, estimatedSize, setVersionInNewDatabase, error, errorMessage,
- AbstractDatabaseServer::RetryOpenDatabase);
- break;
-
- default:
- ASSERT_NOT_REACHED();
+ backend = tryToOpenDatabaseBackend(context, name, expectedVersion, displayName, estimatedSize, setVersionInNewDatabase, RetryOpenDatabase);
}
+ }
- if (!backend) {
- ASSERT(error != DatabaseError::None);
-
- if (error == DatabaseError::InvalidDatabaseState) {
- logErrorMessage(context, errorMessage);
- return 0;
- }
-
+ if (backend.hasException()) {
+ if (backend.exception().code() == INVALID_STATE_ERR)
+ logErrorMessage(context, backend.exception().message());
+ else
logOpenDatabaseError(context, name);
- return 0;
- }
}
- return backend.release();
+ return backend;
}
-void DatabaseManager::addProposedDatabase(ProposedDatabase* proposedDb)
+ExceptionOr<Ref<Database>> DatabaseManager::tryToOpenDatabaseBackend(ScriptExecutionContext& scriptContext, const String& name, const String& expectedVersion, const String& displayName, unsigned estimatedSize, bool setVersionInNewDatabase,
+ OpenAttempt attempt)
{
- std::lock_guard<std::mutex> lock(m_mutex);
+ if (is<Document>(&scriptContext)) {
+ auto* page = downcast<Document>(scriptContext).page();
+ if (!page || page->usesEphemeralSession())
+ return Exception { SECURITY_ERR };
+ }
- m_proposedDatabases.add(proposedDb);
+ if (scriptContext.isWorkerGlobalScope()) {
+ ASSERT_NOT_REACHED();
+ return Exception { SECURITY_ERR };
+ }
+
+ auto backendContext = this->databaseContext(scriptContext);
+
+ ExceptionOr<void> preflightResult;
+ switch (attempt) {
+ case FirstTryToOpenDatabase:
+ preflightResult = DatabaseTracker::singleton().canEstablishDatabase(backendContext, name, estimatedSize);
+ break;
+ case RetryOpenDatabase:
+ preflightResult = DatabaseTracker::singleton().retryCanEstablishDatabase(backendContext, name, estimatedSize);
+ break;
+ }
+ if (preflightResult.hasException())
+ return preflightResult.releaseException();
+
+ auto database = adoptRef(*new Database(backendContext, name, expectedVersion, displayName, estimatedSize));
+
+ auto openResult = database->openAndVerifyVersion(setVersionInNewDatabase);
+ if (openResult.hasException())
+ return openResult.releaseException();
+
+ // FIXME: What guarantees backendContext.securityOrigin() is non-null?
+ DatabaseTracker::singleton().setDatabaseDetails(backendContext->securityOrigin(), name, displayName, estimatedSize);
+ return WTFMove(database);
}
-void DatabaseManager::removeProposedDatabase(ProposedDatabase* proposedDb)
+void DatabaseManager::addProposedDatabase(ProposedDatabase& database)
{
- std::lock_guard<std::mutex> lock(m_mutex);
+ std::lock_guard<Lock> lock { m_proposedDatabasesMutex };
+ m_proposedDatabases.add(&database);
+}
- m_proposedDatabases.remove(proposedDb);
+void DatabaseManager::removeProposedDatabase(ProposedDatabase& database)
+{
+ std::lock_guard<Lock> lock { m_proposedDatabasesMutex };
+ m_proposedDatabases.remove(&database);
}
-PassRefPtr<Database> DatabaseManager::openDatabase(ScriptExecutionContext* context,
- const String& name, const String& expectedVersion, const String& displayName,
- unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback,
- DatabaseError& error)
+ExceptionOr<Ref<Database>> DatabaseManager::openDatabase(ScriptExecutionContext& context, const String& name, const String& expectedVersion, const String& displayName, unsigned estimatedSize, RefPtr<DatabaseCallback>&& creationCallback)
{
ScriptController::initializeThreading();
- ASSERT(error == DatabaseError::None);
bool setVersionInNewDatabase = !creationCallback;
- String errorMessage;
- RefPtr<DatabaseBackendBase> backend = openDatabaseBackend(context, DatabaseType::Async, name,
- expectedVersion, displayName, estimatedSize, setVersionInNewDatabase, error, errorMessage);
- if (!backend)
- return 0;
+ auto openResult = openDatabaseBackend(context, name, expectedVersion, displayName, estimatedSize, setVersionInNewDatabase);
+ if (openResult.hasException())
+ return openResult.releaseException();
- RefPtr<Database> database = Database::create(context, backend);
+ RefPtr<Database> database = openResult.releaseReturnValue();
- RefPtr<DatabaseContext> databaseContext = databaseContextFor(context);
+ auto databaseContext = this->databaseContext(context);
databaseContext->setHasOpenDatabases();
- InspectorInstrumentation::didOpenDatabase(context, database, context->securityOrigin()->host(), name, expectedVersion);
+ InspectorInstrumentation::didOpenDatabase(&context, database.copyRef(), context.securityOrigin()->host(), name, expectedVersion);
- if (backend->isNew() && creationCallback.get()) {
+ if (database->isNew() && creationCallback.get()) {
LOG(StorageAPI, "Scheduling DatabaseCreationCallbackTask for database %p\n", database.get());
- database->m_scriptExecutionContext->postTask(DatabaseCreationCallbackTask::create(database, creationCallback));
- }
-
- ASSERT(database);
- return database.release();
-}
-
-PassRefPtr<DatabaseSync> DatabaseManager::openDatabaseSync(ScriptExecutionContext* context,
- const String& name, const String& expectedVersion, const String& displayName,
- unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback, DatabaseError& error)
-{
- ASSERT(context->isContextThread());
- ASSERT(error == DatabaseError::None);
-
- bool setVersionInNewDatabase = !creationCallback;
- String errorMessage;
- RefPtr<DatabaseBackendBase> backend = openDatabaseBackend(context, DatabaseType::Sync, name,
- expectedVersion, displayName, estimatedSize, setVersionInNewDatabase, error, errorMessage);
- if (!backend)
- return 0;
-
- RefPtr<DatabaseSync> database = DatabaseSync::create(context, backend);
-
- if (backend->isNew() && creationCallback.get()) {
- LOG(StorageAPI, "Invoking the creation callback for database %p\n", database.get());
- creationCallback->handleEvent(database.get());
+ database->setHasPendingCreationEvent(true);
+ database->m_scriptExecutionContext->postTask([creationCallback, database] (ScriptExecutionContext&) {
+ creationCallback->handleEvent(database.get());
+ database->setHasPendingCreationEvent(false);
+ });
}
- ASSERT(database);
- return database.release();
+ return database.releaseNonNull();
}
-bool DatabaseManager::hasOpenDatabases(ScriptExecutionContext* context)
+bool DatabaseManager::hasOpenDatabases(ScriptExecutionContext& context)
{
- RefPtr<DatabaseContext> databaseContext = existingDatabaseContextFor(context);
- if (!databaseContext)
- return false;
- return databaseContext->hasOpenDatabases();
+ auto databaseContext = context.databaseContext();
+ return databaseContext && databaseContext->hasOpenDatabases();
}
-void DatabaseManager::stopDatabases(ScriptExecutionContext* context, DatabaseTaskSynchronizer* synchronizer)
+void DatabaseManager::stopDatabases(ScriptExecutionContext& context, DatabaseTaskSynchronizer* synchronizer)
{
- RefPtr<DatabaseContext> databaseContext = existingDatabaseContextFor(context);
- if (!databaseContext || !databaseContext->stopDatabases(synchronizer))
+ auto databaseContext = context.databaseContext();
+ if (!databaseContext || !databaseContext->stopDatabases(synchronizer)) {
if (synchronizer)
synchronizer->taskCompleted();
+ }
}
-String DatabaseManager::fullPathForDatabase(SecurityOrigin* origin, const String& name, bool createIfDoesNotExist)
+String DatabaseManager::fullPathForDatabase(SecurityOrigin& origin, const String& name, bool createIfDoesNotExist)
{
{
- std::lock_guard<std::mutex> lock(m_mutex);
-
+ std::lock_guard<Lock> lock { m_proposedDatabasesMutex };
for (auto* proposedDatabase : m_proposedDatabases) {
- if (proposedDatabase->details().name() == name && proposedDatabase->origin()->equal(origin))
+ if (proposedDatabase->details().name() == name && proposedDatabase->origin().equal(&origin))
return String();
}
}
-
- return m_server->fullPathForDatabase(origin, name, createIfDoesNotExist);
+ return DatabaseTracker::singleton().fullPathForDatabase(SecurityOriginData::fromSecurityOrigin(origin), name, createIfDoesNotExist);
}
-bool DatabaseManager::hasEntryForOrigin(SecurityOrigin* origin)
-{
- return m_server->hasEntryForOrigin(origin);
-}
-
-void DatabaseManager::origins(Vector<RefPtr<SecurityOrigin>>& result)
-{
- m_server->origins(result);
-}
-
-bool DatabaseManager::databaseNamesForOrigin(SecurityOrigin* origin, Vector<String>& result)
-{
- return m_server->databaseNamesForOrigin(origin, result);
-}
-
-DatabaseDetails DatabaseManager::detailsForNameAndOrigin(const String& name, SecurityOrigin* origin)
+DatabaseDetails DatabaseManager::detailsForNameAndOrigin(const String& name, SecurityOrigin& origin)
{
{
- std::lock_guard<std::mutex> lock(m_mutex);
-
+ std::lock_guard<Lock> lock { m_proposedDatabasesMutex };
for (auto* proposedDatabase : m_proposedDatabases) {
- if (proposedDatabase->details().name() == name && proposedDatabase->origin()->equal(origin)) {
+ if (proposedDatabase->details().name() == name && proposedDatabase->origin().equal(&origin)) {
ASSERT(proposedDatabase->details().threadID() == std::this_thread::get_id() || isMainThread());
-
return proposedDatabase->details();
}
}
}
-
- return m_server->detailsForNameAndOrigin(name, origin);
-}
-
-unsigned long long DatabaseManager::usageForOrigin(SecurityOrigin* origin)
-{
- return m_server->usageForOrigin(origin);
-}
-
-unsigned long long DatabaseManager::quotaForOrigin(SecurityOrigin* origin)
-{
- return m_server->quotaForOrigin(origin);
-}
-void DatabaseManager::setQuota(SecurityOrigin* origin, unsigned long long quotaSize)
-{
- m_server->setQuota(origin, quotaSize);
-}
-
-void DatabaseManager::deleteAllDatabases()
-{
- m_server->deleteAllDatabases();
-}
-
-bool DatabaseManager::deleteOrigin(SecurityOrigin* origin)
-{
- return m_server->deleteOrigin(origin);
+ return DatabaseTracker::singleton().detailsForNameAndOrigin(name, SecurityOriginData::fromSecurityOrigin(origin));
}
-bool DatabaseManager::deleteDatabase(SecurityOrigin* origin, const String& name)
+void DatabaseManager::logErrorMessage(ScriptExecutionContext& context, const String& message)
{
- return m_server->deleteDatabase(origin, name);
-}
-
-void DatabaseManager::interruptAllDatabasesForContext(ScriptExecutionContext* context)
-{
- RefPtr<DatabaseContext> databaseContext = existingDatabaseContextFor(context);
- if (databaseContext)
- m_server->interruptAllDatabasesForContext(databaseContext->backend().get());
-}
-
-void DatabaseManager::logErrorMessage(ScriptExecutionContext* context, const String& message)
-{
- context->addConsoleMessage(StorageMessageSource, ErrorMessageLevel, message);
+ context.addConsoleMessage(MessageSource::Storage, MessageLevel::Error, message);
}
} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)