diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/Modules/webdatabase/DatabaseManager.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/Modules/webdatabase/DatabaseManager.cpp')
-rw-r--r-- | Source/WebCore/Modules/webdatabase/DatabaseManager.cpp | 452 |
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) |