diff options
Diffstat (limited to 'Source/WebCore/Modules/webdatabase')
97 files changed, 2734 insertions, 6533 deletions
diff --git a/Source/WebCore/Modules/webdatabase/AbstractDatabaseServer.h b/Source/WebCore/Modules/webdatabase/AbstractDatabaseServer.h deleted file mode 100644 index 56c74252c..000000000 --- a/Source/WebCore/Modules/webdatabase/AbstractDatabaseServer.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 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. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * 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. - */ - -#ifndef AbstractDatabaseServer_h -#define AbstractDatabaseServer_h - -#if ENABLE(SQL_DATABASE) - -#include "DatabaseBasicTypes.h" -#include "DatabaseDetails.h" -#include "DatabaseError.h" -#include <wtf/RefPtr.h> -#include <wtf/Vector.h> -#include <wtf/text/WTFString.h> - -namespace WebCore { - -class DatabaseBackendBase; -class DatabaseBackendContext; -class DatabaseManagerClient; -class SecurityOrigin; - -class AbstractDatabaseServer { -public: - virtual void initialize(const String& databasePath) = 0; - - virtual void setClient(DatabaseManagerClient*) = 0; - virtual String databaseDirectoryPath() const = 0; - virtual void setDatabaseDirectoryPath(const String&) = 0; - - virtual String fullPathForDatabase(SecurityOrigin*, const String& name, bool createIfDoesNotExist = true) = 0; - - enum OpenAttempt { - FirstTryToOpenDatabase, - RetryOpenDatabase - }; - - virtual PassRefPtr<DatabaseBackendBase> openDatabase(RefPtr<DatabaseBackendContext>&, DatabaseType, - const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize, - bool setVersionInNewDatabase, DatabaseError&, String& errorMessage, OpenAttempt = FirstTryToOpenDatabase) = 0; - - virtual bool hasEntryForOrigin(SecurityOrigin*) = 0; - virtual void origins(Vector<RefPtr<SecurityOrigin>>& result) = 0; - virtual bool databaseNamesForOrigin(SecurityOrigin*, Vector<String>& result) = 0; - virtual DatabaseDetails detailsForNameAndOrigin(const String&, SecurityOrigin*) = 0; - - virtual unsigned long long usageForOrigin(SecurityOrigin*) = 0; - virtual unsigned long long quotaForOrigin(SecurityOrigin*) = 0; - - virtual void setQuota(SecurityOrigin*, unsigned long long) = 0; - - virtual void deleteAllDatabases() = 0; - virtual bool deleteOrigin(SecurityOrigin*) = 0; - virtual bool deleteDatabase(SecurityOrigin*, const String& name) = 0; - - virtual void interruptAllDatabasesForContext(const DatabaseBackendContext*) = 0; - -protected: - AbstractDatabaseServer() { } - virtual ~AbstractDatabaseServer() { } -}; - -} // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) - -#endif // AbstractDatabaseServer_h diff --git a/Source/WebCore/Modules/webdatabase/AbstractSQLStatement.h b/Source/WebCore/Modules/webdatabase/AbstractSQLStatement.h deleted file mode 100644 index 2196a9fc8..000000000 --- a/Source/WebCore/Modules/webdatabase/AbstractSQLStatement.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 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. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * 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. - */ - -#ifndef AbstractSQLStatement_h -#define AbstractSQLStatement_h - -#if ENABLE(SQL_DATABASE) - -#include <wtf/ThreadSafeRefCounted.h> - -namespace WebCore { - -class AbstractSQLStatementBackend; - -class AbstractSQLStatement { -public: - virtual ~AbstractSQLStatement() { } - - virtual void setBackend(AbstractSQLStatementBackend*) = 0; - - virtual bool hasCallback() = 0; - virtual bool hasErrorCallback() = 0; -}; - -} // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) - -#endif // AbstractSQLStatement_h diff --git a/Source/WebCore/Modules/webdatabase/AbstractSQLStatementBackend.h b/Source/WebCore/Modules/webdatabase/AbstractSQLStatementBackend.h deleted file mode 100644 index 045076322..000000000 --- a/Source/WebCore/Modules/webdatabase/AbstractSQLStatementBackend.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 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. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * 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. - */ - -#ifndef AbstractSQLStatementBackend_h -#define AbstractSQLStatementBackend_h - -#if ENABLE(SQL_DATABASE) - -#include "SQLError.h" -#include "SQLResultSet.h" -#include <wtf/ThreadSafeRefCounted.h> - -namespace WebCore { - -class AbstractSQLStatementBackend : public ThreadSafeRefCounted<AbstractSQLStatementBackend> { -public: - virtual ~AbstractSQLStatementBackend() { } - - virtual PassRefPtr<SQLError> sqlError() const = 0; - virtual PassRefPtr<SQLResultSet> sqlResultSet() const = 0; -}; - -} // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) - -#endif // AbstractSQLStatementBackend_h diff --git a/Source/WebCore/Modules/webdatabase/AbstractSQLTransaction.h b/Source/WebCore/Modules/webdatabase/AbstractSQLTransaction.h deleted file mode 100644 index b009587e6..000000000 --- a/Source/WebCore/Modules/webdatabase/AbstractSQLTransaction.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 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. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * 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. - */ - -#ifndef AbstractSQLTransaction_h -#define AbstractSQLTransaction_h - -#if ENABLE(SQL_DATABASE) - -#include "SQLTransactionState.h" -#include <wtf/ThreadSafeRefCounted.h> - -namespace WebCore { - -class AbstractSQLTransactionBackend; - -class AbstractSQLTransaction : public ThreadSafeRefCounted<AbstractSQLTransaction> { -public: - virtual ~AbstractSQLTransaction() { } - - virtual void requestTransitToState(SQLTransactionState) = 0; - - // Used during initialization only. - virtual bool hasCallback() const = 0; - virtual bool hasSuccessCallback() const = 0; - virtual bool hasErrorCallback() const = 0; - virtual void setBackend(AbstractSQLTransactionBackend*) = 0; -}; - -} // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) - -#endif // AbstractSQLTransaction_h diff --git a/Source/WebCore/Modules/webdatabase/AbstractSQLTransactionBackend.h b/Source/WebCore/Modules/webdatabase/AbstractSQLTransactionBackend.h deleted file mode 100644 index 01d9c95dd..000000000 --- a/Source/WebCore/Modules/webdatabase/AbstractSQLTransactionBackend.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 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. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * 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. - */ - -#ifndef AbstractSQLTransactionBackend_h -#define AbstractSQLTransactionBackend_h - -#if ENABLE(SQL_DATABASE) - -#include "AbstractSQLStatement.h" -#include "SQLError.h" -#include "SQLTransactionState.h" -#include "SQLValue.h" -#include <wtf/PassOwnPtr.h> -#include <wtf/ThreadSafeRefCounted.h> -#include <wtf/Vector.h> -#include <wtf/text/WTFString.h> - -namespace WebCore { - -class AbstractSQLTransactionBackend : public ThreadSafeRefCounted<AbstractSQLTransactionBackend> { -public: - virtual ~AbstractSQLTransactionBackend() { } - - virtual void requestTransitToState(SQLTransactionState) = 0; - - virtual PassRefPtr<SQLError> transactionError() = 0; - virtual AbstractSQLStatement* currentStatement() = 0; - virtual void setShouldRetryCurrentStatement(bool) = 0; - - virtual void executeSQL(PassOwnPtr<AbstractSQLStatement>, const String& statement, - const Vector<SQLValue>& arguments, int permissions) = 0; - -}; - -} // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) - -#endif // AbstractSQLTransactionBackend_h diff --git a/Source/WebCore/Modules/webdatabase/ChangeVersionData.h b/Source/WebCore/Modules/webdatabase/ChangeVersionData.h index 2ab196045..deac98fc4 100644 --- a/Source/WebCore/Modules/webdatabase/ChangeVersionData.h +++ b/Source/WebCore/Modules/webdatabase/ChangeVersionData.h @@ -23,10 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ChangeVersionData_h -#define ChangeVersionData_h - -#if ENABLE(SQL_DATABASE) +#pragma once #include <wtf/text/WTFString.h> @@ -46,7 +43,3 @@ private: }; } // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) - -#endif // ChangeVersionData_h diff --git a/Source/WebCore/Modules/webdatabase/ChangeVersionWrapper.cpp b/Source/WebCore/Modules/webdatabase/ChangeVersionWrapper.cpp index 6a397a671..82e780265 100644 --- a/Source/WebCore/Modules/webdatabase/ChangeVersionWrapper.cpp +++ b/Source/WebCore/Modules/webdatabase/ChangeVersionWrapper.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. * @@ -28,11 +28,9 @@ #include "config.h" #include "ChangeVersionWrapper.h" -#if ENABLE(SQL_DATABASE) - #include "Database.h" #include "SQLError.h" -#include <wtf/PassRefPtr.h> +#include "SQLTransactionBackend.h" #include <wtf/RefPtr.h> namespace WebCore { @@ -43,17 +41,14 @@ ChangeVersionWrapper::ChangeVersionWrapper(const String& oldVersion, const Strin { } -bool ChangeVersionWrapper::performPreflight(SQLTransactionBackend* transaction) +bool ChangeVersionWrapper::performPreflight(SQLTransaction& transaction) { - ASSERT(transaction && transaction->database()); - - DatabaseBackend* database = transaction->database(); + Database& database = transaction.database(); String actualVersion; - if (!database->getVersionFromDatabase(actualVersion)) { - int sqliteError = database->sqliteDatabase().lastError(); - m_sqlError = SQLError::create(SQLError::UNKNOWN_ERR, "unable to read the current version", - sqliteError, database->sqliteDatabase().lastErrorMsg()); + if (!database.getVersionFromDatabase(actualVersion)) { + int sqliteError = database.sqliteDatabase().lastError(); + m_sqlError = SQLError::create(SQLError::UNKNOWN_ERR, "unable to read the current version", sqliteError, database.sqliteDatabase().lastErrorMsg()); return false; } @@ -65,28 +60,23 @@ bool ChangeVersionWrapper::performPreflight(SQLTransactionBackend* transaction) return true; } -bool ChangeVersionWrapper::performPostflight(SQLTransactionBackend* transaction) +bool ChangeVersionWrapper::performPostflight(SQLTransaction& transaction) { - ASSERT(transaction && transaction->database()); + Database& database = transaction.database(); - DatabaseBackend* database = transaction->database(); - - if (!database->setVersionInDatabase(m_newVersion)) { - int sqliteError = database->sqliteDatabase().lastError(); - m_sqlError = SQLError::create(SQLError::UNKNOWN_ERR, "unable to set new version in database", - sqliteError, database->sqliteDatabase().lastErrorMsg()); + if (!database.setVersionInDatabase(m_newVersion)) { + int sqliteError = database.sqliteDatabase().lastError(); + m_sqlError = SQLError::create(SQLError::UNKNOWN_ERR, "unable to set new version in database", sqliteError, database.sqliteDatabase().lastErrorMsg()); return false; } - database->setExpectedVersion(m_newVersion); + database.setExpectedVersion(m_newVersion); return true; } -void ChangeVersionWrapper::handleCommitFailedAfterPostflight(SQLTransactionBackend* transaction) +void ChangeVersionWrapper::handleCommitFailedAfterPostflight(SQLTransaction& transaction) { - transaction->database()->setCachedVersion(m_oldVersion); + transaction.database().setCachedVersion(m_oldVersion); } } // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) diff --git a/Source/WebCore/Modules/webdatabase/ChangeVersionWrapper.h b/Source/WebCore/Modules/webdatabase/ChangeVersionWrapper.h index 3b1a2a058..eb7efe3c4 100644 --- a/Source/WebCore/Modules/webdatabase/ChangeVersionWrapper.h +++ b/Source/WebCore/Modules/webdatabase/ChangeVersionWrapper.h @@ -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. * @@ -25,12 +25,10 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ChangeVersionWrapper_h -#define ChangeVersionWrapper_h -#if ENABLE(SQL_DATABASE) +#pragma once -#include "SQLTransactionBackend.h" +#include "SQLTransaction.h" #include <wtf/Forward.h> namespace WebCore { @@ -39,12 +37,12 @@ class SQLError; class ChangeVersionWrapper : public SQLTransactionWrapper { public: - static PassRefPtr<ChangeVersionWrapper> create(const String& oldVersion, const String& newVersion) { return adoptRef(new ChangeVersionWrapper(oldVersion, newVersion)); } + static Ref<ChangeVersionWrapper> create(const String& oldVersion, const String& newVersion) { return adoptRef(*new ChangeVersionWrapper(oldVersion, newVersion)); } - virtual bool performPreflight(SQLTransactionBackend*); - virtual bool performPostflight(SQLTransactionBackend*); - virtual SQLError* sqlError() const { return m_sqlError.get(); } - virtual void handleCommitFailedAfterPostflight(SQLTransactionBackend*); + bool performPreflight(SQLTransaction&) override; + bool performPostflight(SQLTransaction&) override; + SQLError* sqlError() const override { return m_sqlError.get(); }; + void handleCommitFailedAfterPostflight(SQLTransaction&) override; private: ChangeVersionWrapper(const String& oldVersion, const String& newVersion); @@ -55,7 +53,3 @@ private: }; } // namespace WebCore - -#endif - -#endif // ChangeVersionWrapper_h diff --git a/Source/WebCore/Modules/webdatabase/DOMWindowWebDatabase.cpp b/Source/WebCore/Modules/webdatabase/DOMWindowWebDatabase.cpp index 8ccaa5a80..641281ef5 100644 --- a/Source/WebCore/Modules/webdatabase/DOMWindowWebDatabase.cpp +++ b/Source/WebCore/Modules/webdatabase/DOMWindowWebDatabase.cpp @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -25,39 +25,37 @@ */ #include "config.h" - -#if ENABLE(SQL_DATABASE) - #include "DOMWindowWebDatabase.h" #include "DOMWindow.h" #include "Database.h" -#include "DatabaseCallback.h" #include "DatabaseManager.h" #include "Document.h" -#include "Frame.h" +#include "ExceptionCode.h" #include "SecurityOrigin.h" namespace WebCore { -PassRefPtr<Database> DOMWindowWebDatabase::openDatabase(DOMWindow* window, const String& name, const String& version, const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback, ExceptionCode& ec) +ExceptionOr<RefPtr<Database>> DOMWindowWebDatabase::openDatabase(DOMWindow& window, const String& name, const String& version, const String& displayName, unsigned estimatedSize, RefPtr<DatabaseCallback>&& creationCallback) { - if (!window->isCurrentlyDisplayedInFrame()) - return 0; - - RefPtr<Database> database = 0; - DatabaseManager& dbManager = DatabaseManager::manager(); - DatabaseError error = DatabaseError::None; - if (dbManager.isAvailable() && window->document()->securityOrigin()->canAccessDatabase(window->document()->topOrigin())) { - database = dbManager.openDatabase(window->document(), name, version, displayName, estimatedSize, creationCallback, error); - ASSERT(database || error != DatabaseError::None); - ec = DatabaseManager::exceptionCodeForDatabaseError(error); - } else - ec = SECURITY_ERR; - - return database; + if (!window.isCurrentlyDisplayedInFrame()) + return RefPtr<Database> { nullptr }; + auto& manager = DatabaseManager::singleton(); + if (!manager.isAvailable()) + return Exception { SECURITY_ERR }; + auto* document = window.document(); + if (!document) + return Exception { SECURITY_ERR }; + auto& securityOrigin = document->securityOrigin(); + if (!securityOrigin.canAccessDatabase(document->topOrigin())) + return Exception { SECURITY_ERR }; + auto result = manager.openDatabase(*window.document(), name, version, displayName, estimatedSize, WTFMove(creationCallback)); + if (result.hasException()) { + // FIXME: To preserve our past behavior, this discards the error string in the exception. + // At a later time we may decide that we want to use the error strings, and if so we can just return the exception as is. + return Exception { result.releaseException().code() }; + } + return RefPtr<Database> { result.releaseReturnValue() }; } } // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) diff --git a/Source/WebCore/Modules/webdatabase/DOMWindowWebDatabase.h b/Source/WebCore/Modules/webdatabase/DOMWindowWebDatabase.h index 96b6cfc95..5ae95f175 100644 --- a/Source/WebCore/Modules/webdatabase/DOMWindowWebDatabase.h +++ b/Source/WebCore/Modules/webdatabase/DOMWindowWebDatabase.h @@ -11,47 +11,35 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * 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. */ -#ifndef DOMWindowWebDatabase_h -#define DOMWindowWebDatabase_h +#pragma once -#if ENABLE(SQL_DATABASE) -#include "ExceptionCode.h" -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> -#include <wtf/RefPtr.h> -#include <wtf/text/WTFString.h> +#include "ExceptionOr.h" namespace WebCore { class DOMWindow; class Database; class DatabaseCallback; -class Frame; class DOMWindowWebDatabase { public: - static PassRefPtr<Database> openDatabase(DOMWindow*, const String& name, const String& version, const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback, ExceptionCode&); + static ExceptionOr<RefPtr<Database>> openDatabase(DOMWindow&, const String& name, const String& version, const String& displayName, unsigned estimatedSize, RefPtr<DatabaseCallback>&& creationCallback); -private: - DOMWindowWebDatabase() { }; - ~DOMWindowWebDatabase() { }; + DOMWindowWebDatabase() = delete; + ~DOMWindowWebDatabase() = delete; }; } // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) - -#endif // DOMWindowWebDatabase_h diff --git a/Source/WebCore/Modules/webdatabase/DOMWindowWebDatabase.idl b/Source/WebCore/Modules/webdatabase/DOMWindowWebDatabase.idl index 375f0458c..e140cc4c2 100644 --- a/Source/WebCore/Modules/webdatabase/DOMWindowWebDatabase.idl +++ b/Source/WebCore/Modules/webdatabase/DOMWindowWebDatabase.idl @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,9 +24,6 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -[ - Conditional=SQL_DATABASE, -] partial interface DOMWindow { - [RaisesException] Database openDatabase(DOMString name, DOMString version, DOMString displayName, unsigned long estimatedSize, optional DatabaseCallback creationCallback); +partial interface DOMWindow { + [MayThrowException] Database? openDatabase(DOMString name, DOMString version, DOMString displayName, unsigned long estimatedSize, optional DatabaseCallback? creationCallback); }; - diff --git a/Source/WebCore/Modules/webdatabase/Database.cpp b/Source/WebCore/Modules/webdatabase/Database.cpp index e182795be..8869f6bf2 100644 --- a/Source/WebCore/Modules/webdatabase/Database.cpp +++ b/Source/WebCore/Modules/webdatabase/Database.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,11 +29,10 @@ #include "config.h" #include "Database.h" -#if ENABLE(SQL_DATABASE) - #include "ChangeVersionData.h" -#include "CrossThreadTask.h" -#include "DatabaseBackendContext.h" +#include "ChangeVersionWrapper.h" +#include "DOMWindow.h" +#include "DatabaseAuthorizer.h" #include "DatabaseCallback.h" #include "DatabaseContext.h" #include "DatabaseManager.h" @@ -41,196 +40,666 @@ #include "DatabaseThread.h" #include "DatabaseTracker.h" #include "Document.h" +#include "ExceptionCode.h" #include "JSDOMWindow.h" #include "Logging.h" -#include "NotImplemented.h" -#include "Page.h" #include "SQLError.h" #include "SQLTransaction.h" #include "SQLTransactionCallback.h" #include "SQLTransactionErrorCallback.h" +#include "SQLiteDatabaseTracker.h" #include "SQLiteStatement.h" +#include "SQLiteTransaction.h" #include "ScriptExecutionContext.h" #include "SecurityOrigin.h" #include "VoidCallback.h" -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> -#include <wtf/PassRefPtr.h> +#include <wtf/NeverDestroyed.h> #include <wtf/RefPtr.h> #include <wtf/StdLibExtras.h> #include <wtf/text/CString.h> -#if PLATFORM(IOS) -#include "SQLiteDatabaseTracker.h" -#endif - namespace WebCore { -PassRefPtr<Database> Database::create(ScriptExecutionContext*, PassRefPtr<DatabaseBackendBase> backend) +// Registering "opened" databases with the DatabaseTracker +// ======================================================= +// The DatabaseTracker maintains a list of databases that have been +// "opened" so that the client can call interrupt or delete on every database +// associated with a DatabaseContext. +// +// We will only call DatabaseTracker::addOpenDatabase() to add the database +// to the tracker as opened when we've succeeded in opening the database, +// and will set m_opened to true. Similarly, we only call +// DatabaseTracker::removeOpenDatabase() to remove the database from the +// tracker when we set m_opened to false in closeDatabase(). This sets up +// a simple symmetry between open and close operations, and a direct +// correlation to adding and removing databases from the tracker's list, +// thus ensuring that we have a correct list for the interrupt and +// delete operations to work on. +// +// The only databases instances not tracked by the tracker's open database +// list are the ones that have not been added yet, or the ones that we +// attempted an open on but failed to. Such instances only exist in the +// factory functions for creating database backends. +// +// The factory functions will either call openAndVerifyVersion() or +// performOpenAndVerify(). These methods will add the newly instantiated +// database backend if they succeed in opening the requested database. +// In the case of failure to open the database, the factory methods will +// simply discard the newly instantiated database backend when they return. +// The ref counting mechanims will automatically destruct the un-added +// (and un-returned) databases instances. + +static const char versionKey[] = "WebKitDatabaseVersionKey"; +static const char unqualifiedInfoTableName[] = "__WebKitDatabaseInfoTable__"; + +static const char* fullyQualifiedInfoTableName() { - // FIXME: Currently, we're only simulating the backend by return the - // frontend database as its own the backend. When we split the 2 apart, - // this create() function should be changed to be a factory method for - // instantiating the backend. - return static_cast<Database*>(backend.get()); + static const char qualifier[] = "main."; + static char qualifiedName[sizeof(qualifier) + sizeof(unqualifiedInfoTableName) - 1]; + + static std::once_flag onceFlag; + std::call_once(onceFlag, [] { + strcpy(qualifiedName, qualifier); + strcpy(qualifiedName + sizeof(qualifier) - 1, unqualifiedInfoTableName); + }); + + return qualifiedName; } -Database::Database(PassRefPtr<DatabaseBackendContext> databaseContext, - const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize) - : DatabaseBase(databaseContext->scriptExecutionContext()) - , DatabaseBackend(databaseContext, name, expectedVersion, displayName, estimatedSize) - , m_databaseContext(DatabaseBackend::databaseContext()->frontend()) - , m_deleted(false) +static String formatErrorMessage(const char* message, int sqliteErrorCode, const char* sqliteErrorMessage) { - m_databaseThreadSecurityOrigin = m_contextThreadSecurityOrigin->isolatedCopy(); - setFrontend(this); + return String::format("%s (%d %s)", message, sqliteErrorCode, sqliteErrorMessage); +} + +static bool setTextValueInDatabase(SQLiteDatabase& db, const String& query, const String& value) +{ + SQLiteStatement statement(db, query); + int result = statement.prepare(); + + if (result != SQLITE_OK) { + LOG_ERROR("Failed to prepare statement to set value in database (%s)", query.ascii().data()); + return false; + } + + statement.bindText(1, value); + + result = statement.step(); + if (result != SQLITE_DONE) { + LOG_ERROR("Failed to step statement to set value in database (%s)", query.ascii().data()); + return false; + } - ASSERT(m_databaseContext->databaseThread()); + return true; } -class DerefContextTask : public ScriptExecutionContext::Task { -public: - static PassOwnPtr<DerefContextTask> create(PassRefPtr<ScriptExecutionContext> context) - { - return adoptPtr(new DerefContextTask(context)); +static bool retrieveTextResultFromDatabase(SQLiteDatabase& db, const String& query, String& resultString) +{ + SQLiteStatement statement(db, query); + int result = statement.prepare(); + + if (result != SQLITE_OK) { + LOG_ERROR("Error (%i) preparing statement to read text result from database (%s)", result, query.ascii().data()); + return false; } - virtual void performTask(ScriptExecutionContext* context) - { - ASSERT_UNUSED(context, context == m_context); - m_context.clear(); + result = statement.step(); + if (result == SQLITE_ROW) { + resultString = statement.getColumnText(0); + return true; + } + if (result == SQLITE_DONE) { + resultString = String(); + return true; } - virtual bool isCleanupTask() const { return true; } + LOG_ERROR("Error (%i) reading text result from database (%s)", result, query.ascii().data()); + return false; +} -private: - DerefContextTask(PassRefPtr<ScriptExecutionContext> context) - : m_context(context) +// FIXME: move all guid-related functions to a DatabaseVersionTracker class. +static StaticLock guidMutex; + +static HashMap<DatabaseGUID, String>& guidToVersionMap() +{ + static NeverDestroyed<HashMap<DatabaseGUID, String>> map; + return map; +} + +// NOTE: Caller must lock guidMutex(). +static inline void updateGUIDVersionMap(DatabaseGUID guid, const String& newVersion) +{ + // Note: It is not safe to put an empty string into the guidToVersionMap() map. + // That's because the map is cross-thread, but empty strings are per-thread. + // The copy() function makes a version of the string you can use on the current + // thread, but we need a string we can keep in a cross-thread data structure. + // FIXME: This is a quite-awkward restriction to have to program with. + + // Map empty string to null string (see comment above). + guidToVersionMap().set(guid, newVersion.isEmpty() ? String() : newVersion.isolatedCopy()); +} + +static HashMap<DatabaseGUID, HashSet<Database*>>& guidToDatabaseMap() +{ + static NeverDestroyed<HashMap<DatabaseGUID, HashSet<Database*>>> map; + return map; +} + +static inline DatabaseGUID guidForOriginAndName(const String& origin, const String& name) +{ + static NeverDestroyed<HashMap<String, DatabaseGUID>> map; + return map.get().ensure(makeString(origin, '/', name), [] { + static DatabaseGUID lastUsedGUID; + return ++lastUsedGUID; + }).iterator->value; +} + +Database::Database(DatabaseContext& context, const String& name, const String& expectedVersion, const String& displayName, unsigned estimatedSize) + : m_scriptExecutionContext(*context.scriptExecutionContext()) + , m_contextThreadSecurityOrigin(m_scriptExecutionContext->securityOrigin()->isolatedCopy()) + , m_databaseThreadSecurityOrigin(m_scriptExecutionContext->securityOrigin()->isolatedCopy()) + , m_databaseContext(context) + , m_name((name.isNull() ? emptyString() : name).isolatedCopy()) + , m_expectedVersion(expectedVersion.isolatedCopy()) + , m_displayName(displayName.isolatedCopy()) + , m_estimatedSize(estimatedSize) + , m_filename(DatabaseManager::singleton().fullPathForDatabase(*m_scriptExecutionContext->securityOrigin(), m_name)) + , m_databaseAuthorizer(DatabaseAuthorizer::create(unqualifiedInfoTableName)) +{ { + std::lock_guard<StaticLock> locker(guidMutex); + + m_guid = guidForOriginAndName(securityOrigin().securityOrigin()->toString(), name); + guidToDatabaseMap().ensure(m_guid, [] { + return HashSet<Database*>(); + }).iterator->value.add(this); } - - RefPtr<ScriptExecutionContext> m_context; -}; + + m_databaseContext->databaseThread(); + + ASSERT(m_databaseContext->existingDatabaseThread()); +} + +DatabaseThread& Database::databaseThread() +{ + ASSERT(m_databaseContext->existingDatabaseThread()); + return *m_databaseContext->existingDatabaseThread(); +} Database::~Database() { // The reference to the ScriptExecutionContext needs to be cleared on the JavaScript thread. If we're on that thread already, we can just let the RefPtr's destruction do the dereffing. if (!m_scriptExecutionContext->isContextThread()) { - // Grab a pointer to the script execution here because we're releasing it when we pass it to - // DerefContextTask::create. - ScriptExecutionContext* scriptExecutionContext = m_scriptExecutionContext.get(); - - scriptExecutionContext->postTask(DerefContextTask::create(m_scriptExecutionContext.release())); + auto passedContext = WTFMove(m_scriptExecutionContext); + auto& contextRef = passedContext.get(); + contextRef.postTask({ScriptExecutionContext::Task::CleanupTask, [passedContext = WTFMove(passedContext), databaseContext = WTFMove(m_databaseContext)] (ScriptExecutionContext& context) { + ASSERT_UNUSED(context, &context == passedContext.ptr()); + }}); } + + // SQLite is "multi-thread safe", but each database handle can only be used + // on a single thread at a time. + // + // For DatabaseBackend, we open the SQLite database on the DatabaseThread, + // and hence we should also close it on that same thread. This means that the + // SQLite database need to be closed by another mechanism (see + // DatabaseContext::stopDatabases()). By the time we get here, the SQLite + // database should have already been closed. + + ASSERT(!m_opened); } -Database* Database::from(DatabaseBackend* backend) +ExceptionOr<void> Database::openAndVerifyVersion(bool setVersionInNewDatabase) { - return static_cast<Database*>(backend->m_frontend); + DatabaseTaskSynchronizer synchronizer; + auto& thread = databaseThread(); + if (thread.terminationRequested(&synchronizer)) + return Exception { INVALID_STATE_ERR }; + + ExceptionOr<void> result; + auto task = std::make_unique<DatabaseOpenTask>(*this, setVersionInNewDatabase, synchronizer, result); + thread.scheduleImmediateTask(WTFMove(task)); + synchronizer.waitForTaskCompletion(); + + return result; } -PassRefPtr<DatabaseBackend> Database::backend() +void Database::interrupt() { - return this; + // It is safe to call this from any thread for an opened or closed database. + m_sqliteDatabase.interrupt(); +} + +void Database::close() +{ + auto& thread = databaseThread(); + + DatabaseTaskSynchronizer synchronizer; + if (thread.terminationRequested(&synchronizer)) { + LOG(StorageAPI, "Database handle %p is on a terminated DatabaseThread, cannot be marked for normal closure\n", this); + return; + } + + thread.scheduleImmediateTask(std::make_unique<DatabaseCloseTask>(*this, synchronizer)); + + // FIXME: iOS depends on this function blocking until the database is closed as part + // of closing all open databases from a process assertion expiration handler. + // See <https://bugs.webkit.org/show_bug.cgi?id=157184>. + synchronizer.waitForTaskCompletion(); +} + +void Database::performClose() +{ + ASSERT(currentThread() == databaseThread().getThreadID()); + + { + LockHolder locker(m_transactionInProgressMutex); + + // Clean up transactions that have not been scheduled yet: + // Transaction phase 1 cleanup. See comment on "What happens if a + // transaction is interrupted?" at the top of SQLTransactionBackend.cpp. + while (!m_transactionQueue.isEmpty()) + m_transactionQueue.takeFirst()->notifyDatabaseThreadIsShuttingDown(); + + m_isTransactionQueueEnabled = false; + m_transactionInProgress = false; + } + + closeDatabase(); + + // DatabaseThread keeps databases alive by referencing them in its + // m_openDatabaseSet. DatabaseThread::recordDatabaseClose() will remove + // this database from that set (which effectively deref's it). We hold on + // to it with a local pointer here for a liitle longer, so that we can + // unschedule any DatabaseTasks that refer to it before the database gets + // deleted. + Ref<Database> protectedThis(*this); + auto& thread = databaseThread(); + thread.recordDatabaseClosed(*this); + thread.unscheduleDatabaseTasks(*this); +} + +class DoneCreatingDatabaseOnExitCaller { +public: + DoneCreatingDatabaseOnExitCaller(Database& database) + : m_database(database) + { + } + + ~DoneCreatingDatabaseOnExitCaller() + { + DatabaseTracker::singleton().doneCreatingDatabase(m_database); + } + +private: + Database& m_database; +}; + +ExceptionOr<void> Database::performOpenAndVerify(bool shouldSetVersionInNewDatabase) +{ + DoneCreatingDatabaseOnExitCaller onExitCaller(*this); + + const int maxSqliteBusyWaitTime = 30000; + +#if PLATFORM(IOS) + { + // Make sure we wait till the background removal of the empty database files finished before trying to open any database. + LockHolder locker(DatabaseTracker::openDatabaseMutex()); + } +#endif + + SQLiteTransactionInProgressAutoCounter transactionCounter; + + if (!m_sqliteDatabase.open(m_filename, true)) + return Exception { INVALID_STATE_ERR, formatErrorMessage("unable to open database", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()) }; + if (!m_sqliteDatabase.turnOnIncrementalAutoVacuum()) + LOG_ERROR("Unable to turn on incremental auto-vacuum (%d %s)", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); + + m_sqliteDatabase.setBusyTimeout(maxSqliteBusyWaitTime); + + String currentVersion; + { + std::lock_guard<StaticLock> locker(guidMutex); + + auto entry = guidToVersionMap().find(m_guid); + if (entry != guidToVersionMap().end()) { + // Map null string to empty string (see updateGUIDVersionMap()). + currentVersion = entry->value.isNull() ? emptyString() : entry->value.isolatedCopy(); + LOG(StorageAPI, "Current cached version for guid %i is %s", m_guid, currentVersion.ascii().data()); + } else { + LOG(StorageAPI, "No cached version for guid %i", m_guid); + + SQLiteTransaction transaction(m_sqliteDatabase); + transaction.begin(); + if (!transaction.inProgress()) { + String message = formatErrorMessage("unable to open database, failed to start transaction", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); + m_sqliteDatabase.close(); + return Exception { INVALID_STATE_ERR, WTFMove(message) }; + } + + String tableName(unqualifiedInfoTableName); + if (!m_sqliteDatabase.tableExists(tableName)) { + m_new = true; + + if (!m_sqliteDatabase.executeCommand("CREATE TABLE " + tableName + " (key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,value TEXT NOT NULL ON CONFLICT FAIL);")) { + String message = formatErrorMessage("unable to open database, failed to create 'info' table", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); + transaction.rollback(); + m_sqliteDatabase.close(); + return Exception { INVALID_STATE_ERR, WTFMove(message) }; + } + } else if (!getVersionFromDatabase(currentVersion, false)) { + String message = formatErrorMessage("unable to open database, failed to read current version", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); + transaction.rollback(); + m_sqliteDatabase.close(); + return Exception { INVALID_STATE_ERR, WTFMove(message) }; + } + + if (currentVersion.length()) { + LOG(StorageAPI, "Retrieved current version %s from database %s", currentVersion.ascii().data(), databaseDebugName().ascii().data()); + } else if (!m_new || shouldSetVersionInNewDatabase) { + LOG(StorageAPI, "Setting version %s in database %s that was just created", m_expectedVersion.ascii().data(), databaseDebugName().ascii().data()); + if (!setVersionInDatabase(m_expectedVersion, false)) { + String message = formatErrorMessage("unable to open database, failed to write current version", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); + transaction.rollback(); + m_sqliteDatabase.close(); + return Exception { INVALID_STATE_ERR, WTFMove(message) }; + } + currentVersion = m_expectedVersion; + } + updateGUIDVersionMap(m_guid, currentVersion); + transaction.commit(); + } + } + + if (currentVersion.isNull()) { + LOG(StorageAPI, "Database %s does not have its version set", databaseDebugName().ascii().data()); + currentVersion = emptyString(); + } + + // If the expected version isn't the empty string, ensure that the current database version we have matches that version. Otherwise, set an exception. + // If the expected version is the empty string, then we always return with whatever version of the database we have. + if ((!m_new || shouldSetVersionInNewDatabase) && m_expectedVersion.length() && m_expectedVersion != currentVersion) { + m_sqliteDatabase.close(); + return Exception { INVALID_STATE_ERR, "unable to open database, version mismatch, '" + m_expectedVersion + "' does not match the currentVersion of '" + currentVersion + "'" }; + } + + m_sqliteDatabase.setAuthorizer(m_databaseAuthorizer.get()); + + DatabaseTracker::singleton().addOpenDatabase(*this); + m_opened = true; + + if (m_new && !shouldSetVersionInNewDatabase) + m_expectedVersion = emptyString(); // The caller provided a creationCallback which will set the expected version. + + databaseThread().recordDatabaseOpen(*this); + + return { }; +} + +void Database::closeDatabase() +{ + if (!m_opened) + return; + + m_sqliteDatabase.close(); + m_opened = false; + + // See comment at the top this file regarding calling removeOpenDatabase(). + DatabaseTracker::singleton().removeOpenDatabase(*this); + + { + std::lock_guard<StaticLock> locker(guidMutex); + + auto it = guidToDatabaseMap().find(m_guid); + ASSERT(it != guidToDatabaseMap().end()); + ASSERT(it->value.contains(this)); + it->value.remove(this); + if (it->value.isEmpty()) { + guidToDatabaseMap().remove(it); + guidToVersionMap().remove(m_guid); + } + } +} + +bool Database::getVersionFromDatabase(String& version, bool shouldCacheVersion) +{ + String query(String("SELECT value FROM ") + fullyQualifiedInfoTableName() + " WHERE key = '" + versionKey + "';"); + + m_databaseAuthorizer->disable(); + + bool result = retrieveTextResultFromDatabase(m_sqliteDatabase, query, version); + if (result) { + if (shouldCacheVersion) + setCachedVersion(version); + } else + LOG_ERROR("Failed to retrieve version from database %s", databaseDebugName().ascii().data()); + + m_databaseAuthorizer->enable(); + + return result; +} + +bool Database::setVersionInDatabase(const String& version, bool shouldCacheVersion) +{ + // The INSERT will replace an existing entry for the database with the new version number, due to the UNIQUE ON CONFLICT REPLACE + // clause in the CREATE statement (see Database::performOpenAndVerify()). + String query(String("INSERT INTO ") + fullyQualifiedInfoTableName() + " (key, value) VALUES ('" + versionKey + "', ?);"); + + m_databaseAuthorizer->disable(); + + bool result = setTextValueInDatabase(m_sqliteDatabase, query, version); + if (result) { + if (shouldCacheVersion) + setCachedVersion(version); + } else + LOG_ERROR("Failed to set version %s in database (%s)", version.ascii().data(), query.ascii().data()); + + m_databaseAuthorizer->enable(); + + return result; +} + +void Database::setExpectedVersion(const String& version) +{ + m_expectedVersion = version.isolatedCopy(); +} + +String Database::getCachedVersion() const +{ + std::lock_guard<StaticLock> locker(guidMutex); + + return guidToVersionMap().get(m_guid).isolatedCopy(); +} + +void Database::setCachedVersion(const String& actualVersion) +{ + std::lock_guard<StaticLock> locker(guidMutex); + + updateGUIDVersionMap(m_guid, actualVersion); +} + +bool Database::getActualVersionForTransaction(String &actualVersion) +{ + ASSERT(m_sqliteDatabase.transactionInProgress()); + + // Note: In multi-process browsers the cached value may be inaccurate. + // So we retrieve the value from the database and update the cached value here. + return getVersionFromDatabase(actualVersion, true); +} + +void Database::scheduleTransaction() +{ + ASSERT(!m_transactionInProgressMutex.tryLock()); // Locked by caller. + + if (!m_isTransactionQueueEnabled || m_transactionQueue.isEmpty()) { + m_transactionInProgress = false; + return; + } + + m_transactionInProgress = true; + + auto transaction = m_transactionQueue.takeFirst(); + auto task = std::make_unique<DatabaseTransactionTask>(WTFMove(transaction)); + LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for transaction %p\n", task.get(), task->transaction()); + databaseThread().scheduleTask(WTFMove(task)); +} + +void Database::scheduleTransactionStep(SQLTransaction& transaction) +{ + auto& thread = databaseThread(); + + auto task = std::make_unique<DatabaseTransactionTask>(&transaction); + LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for the transaction step\n", task.get()); + thread.scheduleTask(WTFMove(task)); +} + +void Database::inProgressTransactionCompleted() +{ + LockHolder locker(m_transactionInProgressMutex); + m_transactionInProgress = false; + scheduleTransaction(); +} + +bool Database::hasPendingTransaction() +{ + LockHolder locker(m_transactionInProgressMutex); + return m_transactionInProgress || !m_transactionQueue.isEmpty(); +} + +SQLTransactionCoordinator* Database::transactionCoordinator() +{ + return databaseThread().transactionCoordinator(); } String Database::version() const { if (m_deleted) return String(); - return DatabaseBackendBase::version(); + + // Note: In multi-process browsers the cached value may be accurate, but we cannot read the + // actual version from the database without potentially inducing a deadlock. + // FIXME: Add an async version getter to the DatabaseAPI. + return getCachedVersion(); } void Database::markAsDeletedAndClose() { - if (m_deleted || !databaseContext()->databaseThread()) + if (m_deleted) return; LOG(StorageAPI, "Marking %s (%p) as deleted", stringIdentifier().ascii().data(), this); m_deleted = true; - DatabaseTaskSynchronizer synchronizer; - if (databaseContext()->databaseThread()->terminationRequested(&synchronizer)) { - LOG(StorageAPI, "Database handle %p is on a terminated DatabaseThread, cannot be marked for normal closure\n", this); - return; - } + close(); +} - auto task = DatabaseCloseTask::create(this, &synchronizer); - databaseContext()->databaseThread()->scheduleImmediateTask(std::move(task)); - synchronizer.waitForTaskCompletion(); +void Database::changeVersion(const String& oldVersion, const String& newVersion, RefPtr<SQLTransactionCallback>&& callback, RefPtr<SQLTransactionErrorCallback>&& errorCallback, RefPtr<VoidCallback>&& successCallback) +{ + runTransaction(WTFMove(callback), WTFMove(errorCallback), WTFMove(successCallback), ChangeVersionWrapper::create(oldVersion, newVersion), false); } -void Database::closeImmediately() +void Database::transaction(RefPtr<SQLTransactionCallback>&& callback, RefPtr<SQLTransactionErrorCallback>&& errorCallback, RefPtr<VoidCallback>&& successCallback) { - ASSERT(m_scriptExecutionContext->isContextThread()); - DatabaseThread* databaseThread = databaseContext()->databaseThread(); - if (databaseThread && !databaseThread->terminationRequested() && opened()) { - logErrorMessage("forcibly closing database"); - databaseThread->scheduleImmediateTask(DatabaseCloseTask::create(this, 0)); - } + runTransaction(WTFMove(callback), WTFMove(errorCallback), WTFMove(successCallback), nullptr, false); } -void Database::changeVersion(const String& oldVersion, const String& newVersion, - PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, - PassRefPtr<VoidCallback> successCallback) +void Database::readTransaction(RefPtr<SQLTransactionCallback>&& callback, RefPtr<SQLTransactionErrorCallback>&& errorCallback, RefPtr<VoidCallback>&& successCallback) { - ChangeVersionData data(oldVersion, newVersion); - runTransaction(callback, errorCallback, successCallback, false, &data); + runTransaction(WTFMove(callback), WTFMove(errorCallback), WTFMove(successCallback), nullptr, true); } -void Database::transaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback) +String Database::stringIdentifier() const { - runTransaction(callback, errorCallback, successCallback, false); + // Return a deep copy for ref counting thread safety + return m_name.isolatedCopy(); } -void Database::readTransaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback) +String Database::displayName() const { - runTransaction(callback, errorCallback, successCallback, true); + // Return a deep copy for ref counting thread safety + return m_displayName.isolatedCopy(); } -static void callTransactionErrorCallback(ScriptExecutionContext*, PassRefPtr<SQLTransactionErrorCallback> callback, PassRefPtr<SQLError> error) +unsigned Database::estimatedSize() const { - callback->handleEvent(error.get()); + return m_estimatedSize; } -void Database::runTransaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, - PassRefPtr<VoidCallback> successCallback, bool readOnly, const ChangeVersionData* changeVersionData) +String Database::fileName() const { - RefPtr<SQLTransactionErrorCallback> anotherRefToErrorCallback = errorCallback; - RefPtr<SQLTransaction> transaction = SQLTransaction::create(this, callback, successCallback, anotherRefToErrorCallback, readOnly); + // Return a deep copy for ref counting thread safety + return m_filename.isolatedCopy(); +} - RefPtr<SQLTransactionBackend> transactionBackend; - transactionBackend = backend()->runTransaction(transaction.release(), readOnly, changeVersionData); - if (!transactionBackend && anotherRefToErrorCallback) { - RefPtr<SQLError> error = SQLError::create(SQLError::UNKNOWN_ERR, "database has been closed"); - scriptExecutionContext()->postTask(createCallbackTask(&callTransactionErrorCallback, anotherRefToErrorCallback, error.release())); - } +DatabaseDetails Database::details() const +{ + // This code path is only used for database quota delegate calls, so file dates are irrelevant and left uninitialized. + return DatabaseDetails(stringIdentifier(), displayName(), estimatedSize(), 0, 0, 0); } -class DeliverPendingCallbackTask : public ScriptExecutionContext::Task { -public: - static PassOwnPtr<DeliverPendingCallbackTask> create(PassRefPtr<SQLTransaction> transaction) - { - return adoptPtr(new DeliverPendingCallbackTask(transaction)); - } +void Database::disableAuthorizer() +{ + m_databaseAuthorizer->disable(); +} - virtual void performTask(ScriptExecutionContext*) - { - m_transaction->performPendingCallback(); - } +void Database::enableAuthorizer() +{ + m_databaseAuthorizer->enable(); +} -private: - DeliverPendingCallbackTask(PassRefPtr<SQLTransaction> transaction) - : m_transaction(transaction) - { +void Database::setAuthorizerPermissions(int permissions) +{ + m_databaseAuthorizer->setPermissions(permissions); +} + +bool Database::lastActionChangedDatabase() +{ + return m_databaseAuthorizer->lastActionChangedDatabase(); +} + +bool Database::lastActionWasInsert() +{ + return m_databaseAuthorizer->lastActionWasInsert(); +} + +void Database::resetDeletes() +{ + m_databaseAuthorizer->resetDeletes(); +} + +bool Database::hadDeletes() +{ + return m_databaseAuthorizer->hadDeletes(); +} + +void Database::resetAuthorizer() +{ + m_databaseAuthorizer->reset(); +} + +void Database::runTransaction(RefPtr<SQLTransactionCallback>&& callback, RefPtr<SQLTransactionErrorCallback>&& errorCallback, RefPtr<VoidCallback>&& successCallback, RefPtr<SQLTransactionWrapper>&& wrapper, bool readOnly) +{ + LockHolder locker(m_transactionInProgressMutex); + if (!m_isTransactionQueueEnabled) { + if (errorCallback) { + RefPtr<SQLTransactionErrorCallback> errorCallbackProtector = WTFMove(errorCallback); + m_scriptExecutionContext->postTask([errorCallbackProtector](ScriptExecutionContext&) { + errorCallbackProtector->handleEvent(SQLError::create(SQLError::UNKNOWN_ERR, "database has been closed").ptr()); + }); + } + return; } - RefPtr<SQLTransaction> m_transaction; -}; + m_transactionQueue.append(SQLTransaction::create(*this, WTFMove(callback), WTFMove(successCallback), errorCallback.copyRef(), WTFMove(wrapper), readOnly)); + if (!m_transactionInProgress) + scheduleTransaction(); +} void Database::scheduleTransactionCallback(SQLTransaction* transaction) { - m_scriptExecutionContext->postTask(DeliverPendingCallbackTask::create(transaction)); + RefPtr<SQLTransaction> transactionProtector(transaction); + m_scriptExecutionContext->postTask([transactionProtector] (ScriptExecutionContext&) { + transactionProtector->performPendingCallback(); + }); } Vector<String> Database::performGetTableNames() @@ -238,7 +707,7 @@ Vector<String> Database::performGetTableNames() disableAuthorizer(); SQLiteStatement statement(sqliteDatabase(), "SELECT name FROM sqlite_master WHERE type='table';"); - if (statement.prepare() != SQLResultOk) { + if (statement.prepare() != SQLITE_OK) { LOG_ERROR("Unable to retrieve list of tables for database %s", databaseDebugName().ascii().data()); enableAuthorizer(); return Vector<String>(); @@ -246,15 +715,15 @@ Vector<String> Database::performGetTableNames() Vector<String> tableNames; int result; - while ((result = statement.step()) == SQLResultRow) { + while ((result = statement.step()) == SQLITE_ROW) { String name = statement.getColumnText(0); - if (name != databaseInfoTableName()) + if (name != unqualifiedInfoTableName) tableNames.append(name); } enableAuthorizer(); - if (result != SQLResultDone) { + if (result != SQLITE_DONE) { LOG_ERROR("Error getting tables for database %s", databaseDebugName().ascii().data()); return Vector<String>(); } @@ -262,31 +731,77 @@ Vector<String> Database::performGetTableNames() return tableNames; } +void Database::incrementalVacuumIfNeeded() +{ + SQLiteTransactionInProgressAutoCounter transactionCounter; + + int64_t freeSpaceSize = m_sqliteDatabase.freeSpaceSize(); + int64_t totalSize = m_sqliteDatabase.totalSize(); + if (totalSize <= 10 * freeSpaceSize) { + int result = m_sqliteDatabase.runIncrementalVacuumCommand(); + if (result != SQLITE_OK) + logErrorMessage(formatErrorMessage("error vacuuming database", result, m_sqliteDatabase.lastErrorMsg())); + } +} + +void Database::logErrorMessage(const String& message) +{ + m_scriptExecutionContext->addConsoleMessage(MessageSource::Storage, MessageLevel::Error, message); +} + Vector<String> Database::tableNames() { // FIXME: Not using isolatedCopy on these strings looks ok since threads take strict turns // in dealing with them. However, if the code changes, this may not be true anymore. Vector<String> result; DatabaseTaskSynchronizer synchronizer; - if (!databaseContext()->databaseThread() || databaseContext()->databaseThread()->terminationRequested(&synchronizer)) + auto& thread = databaseThread(); + if (thread.terminationRequested(&synchronizer)) return result; - auto task = DatabaseTableNamesTask::create(this, &synchronizer, result); - databaseContext()->databaseThread()->scheduleImmediateTask(std::move(task)); + auto task = std::make_unique<DatabaseTableNamesTask>(*this, synchronizer, result); + thread.scheduleImmediateTask(WTFMove(task)); synchronizer.waitForTaskCompletion(); return result; } -SecurityOrigin* Database::securityOrigin() const +SecurityOriginData Database::securityOrigin() { if (m_scriptExecutionContext->isContextThread()) - return m_contextThreadSecurityOrigin.get(); - if (currentThread() == databaseContext()->databaseThread()->getThreadID()) - return m_databaseThreadSecurityOrigin.get(); - return 0; + return SecurityOriginData::fromSecurityOrigin(m_contextThreadSecurityOrigin.get()); + auto& thread = databaseThread(); + if (currentThread() == thread.getThreadID()) + return SecurityOriginData::fromSecurityOrigin(m_databaseThreadSecurityOrigin.get()); + RELEASE_ASSERT_NOT_REACHED(); } -} // namespace WebCore +unsigned long long Database::maximumSize() +{ + return DatabaseTracker::singleton().maximumSize(*this); +} + +void Database::didCommitWriteTransaction() +{ + DatabaseTracker::singleton().scheduleNotifyDatabaseChanged(securityOrigin(), stringIdentifier()); +} + +bool Database::didExceedQuota() +{ + ASSERT(databaseContext().scriptExecutionContext()->isContextThread()); + auto& tracker = DatabaseTracker::singleton(); + auto oldQuota = tracker.quota(securityOrigin()); + databaseContext().databaseExceededQuota(stringIdentifier(), details()); + return tracker.quota(securityOrigin()) > oldQuota; +} -#endif // ENABLE(SQL_DATABASE) +#if !LOG_DISABLED || !ERROR_DISABLED + +String Database::databaseDebugName() const +{ + return m_contextThreadSecurityOrigin->toString() + "::" + m_name; +} + +#endif + +} // namespace WebCore diff --git a/Source/WebCore/Modules/webdatabase/Database.h b/Source/WebCore/Modules/webdatabase/Database.h index 068468acb..348d6ae2f 100644 --- a/Source/WebCore/Modules/webdatabase/Database.h +++ b/Source/WebCore/Modules/webdatabase/Database.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2013, 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -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. * @@ -26,80 +26,157 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef Database_h -#define Database_h +#pragma once -#if ENABLE(SQL_DATABASE) - -#include "DatabaseBackend.h" -#include "DatabaseBase.h" -#include "DatabaseBasicTypes.h" -#include "DatabaseError.h" -#include <wtf/text/WTFString.h> +#include "ExceptionOr.h" +#include "SQLiteDatabase.h" +#include <wtf/Deque.h> +#include <wtf/Lock.h> namespace WebCore { -class ChangeVersionData; class DatabaseCallback; class DatabaseContext; +class DatabaseDetails; +class DatabaseThread; +class ScriptExecutionContext; class SecurityOrigin; class SQLTransaction; class SQLTransactionBackend; class SQLTransactionCallback; +class SQLTransactionCoordinator; class SQLTransactionErrorCallback; +class SQLTransactionWrapper; class VoidCallback; +struct SecurityOriginData; + +using DatabaseGUID = int; -class Database : public DatabaseBase, public DatabaseBackend { +class Database : public ThreadSafeRefCounted<Database> { public: - virtual ~Database(); + ~Database(); + + ExceptionOr<void> openAndVerifyVersion(bool setVersionInNewDatabase); + void close(); + + void interrupt(); + + bool opened() const { return m_opened; } + bool isNew() const { return m_new; } + + unsigned long long maximumSize(); + + void scheduleTransactionStep(SQLTransaction&); + void inProgressTransactionCompleted(); + + bool hasPendingTransaction(); + + bool hasPendingCreationEvent() const { return m_hasPendingCreationEvent; } + void setHasPendingCreationEvent(bool value) { m_hasPendingCreationEvent = value; } + + void didCommitWriteTransaction(); + bool didExceedQuota(); + + SQLTransactionCoordinator* transactionCoordinator(); // Direct support for the DOM API - virtual String version() const; - void changeVersion(const String& oldVersion, const String& newVersion, PassRefPtr<SQLTransactionCallback>, - PassRefPtr<SQLTransactionErrorCallback>, PassRefPtr<VoidCallback> successCallback); - void transaction(PassRefPtr<SQLTransactionCallback>, PassRefPtr<SQLTransactionErrorCallback>, PassRefPtr<VoidCallback> successCallback); - void readTransaction(PassRefPtr<SQLTransactionCallback>, PassRefPtr<SQLTransactionErrorCallback>, PassRefPtr<VoidCallback> successCallback); + String version() const; + void changeVersion(const String& oldVersion, const String& newVersion, RefPtr<SQLTransactionCallback>&&, RefPtr<SQLTransactionErrorCallback>&&, RefPtr<VoidCallback>&& successCallback); + void transaction(RefPtr<SQLTransactionCallback>&&, RefPtr<SQLTransactionErrorCallback>&&, RefPtr<VoidCallback>&& successCallback); + void readTransaction(RefPtr<SQLTransactionCallback>&&, RefPtr<SQLTransactionErrorCallback>&&, RefPtr<VoidCallback>&& successCallback); // Internal engine support - static Database* from(DatabaseBackend*); - DatabaseContext* databaseContext() const { return m_databaseContext.get(); } + String stringIdentifier() const; + String displayName() const; + unsigned estimatedSize() const; + String fileName() const; + DatabaseDetails details() const; + SQLiteDatabase& sqliteDatabase() { return m_sqliteDatabase; } + + void disableAuthorizer(); + void enableAuthorizer(); + void setAuthorizerPermissions(int); + bool lastActionChangedDatabase(); + bool lastActionWasInsert(); + void resetDeletes(); + bool hadDeletes(); + void resetAuthorizer(); + + DatabaseContext& databaseContext() { return m_databaseContext; } + DatabaseThread& databaseThread(); + ScriptExecutionContext& scriptExecutionContext() { return m_scriptExecutionContext; } + void logErrorMessage(const String& message); Vector<String> tableNames(); - virtual SecurityOrigin* securityOrigin() const; + SecurityOriginData securityOrigin(); - virtual void markAsDeletedAndClose(); + void markAsDeletedAndClose(); bool deleted() const { return m_deleted; } - virtual void closeImmediately(); - void scheduleTransactionCallback(SQLTransaction*); + void incrementalVacuumIfNeeded(); + + // Called from DatabaseTask + ExceptionOr<void> performOpenAndVerify(bool shouldSetVersionInNewDatabase); + Vector<String> performGetTableNames(); + + // Called from DatabaseTask and DatabaseThread + void performClose(); + private: - Database(PassRefPtr<DatabaseBackendContext>, const String& name, - const String& expectedVersion, const String& displayName, unsigned long estimatedSize); - PassRefPtr<DatabaseBackend> backend(); - static PassRefPtr<Database> create(ScriptExecutionContext*, PassRefPtr<DatabaseBackendBase>); + Database(DatabaseContext&, const String& name, const String& expectedVersion, const String& displayName, unsigned estimatedSize); - void runTransaction(PassRefPtr<SQLTransactionCallback>, PassRefPtr<SQLTransactionErrorCallback>, - PassRefPtr<VoidCallback> successCallback, bool readOnly, const ChangeVersionData* = 0); + void closeDatabase(); - Vector<String> performGetTableNames(); + bool getVersionFromDatabase(String& version, bool shouldCacheVersion = true); + bool setVersionInDatabase(const String& version, bool shouldCacheVersion = true); + void setExpectedVersion(const String&); + const String& expectedVersion() const { return m_expectedVersion; } + String getCachedVersion() const; + void setCachedVersion(const String&); + bool getActualVersionForTransaction(String& version); + + void scheduleTransaction(); + + void runTransaction(RefPtr<SQLTransactionCallback>&&, RefPtr<SQLTransactionErrorCallback>&&, RefPtr<VoidCallback>&& successCallback, RefPtr<SQLTransactionWrapper>&&, bool readOnly); + +#if !LOG_DISABLED || !ERROR_DISABLED + String databaseDebugName() const; +#endif + + Ref<ScriptExecutionContext> m_scriptExecutionContext; + Ref<SecurityOrigin> m_contextThreadSecurityOrigin; + Ref<SecurityOrigin> m_databaseThreadSecurityOrigin; + Ref<DatabaseContext> m_databaseContext; - RefPtr<SecurityOrigin> m_databaseThreadSecurityOrigin; - RefPtr<DatabaseContext> m_databaseContext; + bool m_deleted { false }; + bool m_hasPendingCreationEvent { false }; - bool m_deleted; + String m_name; + String m_expectedVersion; + String m_displayName; + unsigned m_estimatedSize; + String m_filename; + DatabaseGUID m_guid; + bool m_opened { false }; + bool m_new { false }; + + SQLiteDatabase m_sqliteDatabase; + + Ref<DatabaseAuthorizer> m_databaseAuthorizer; + + Deque<Ref<SQLTransaction>> m_transactionQueue; + Lock m_transactionInProgressMutex; + bool m_transactionInProgress { false }; + bool m_isTransactionQueueEnabled { true }; + + friend class ChangeVersionWrapper; friend class DatabaseManager; - friend class DatabaseServer; // FIXME: remove this when the backend has been split out. - friend class DatabaseBackend; // FIXME: remove this when the backend has been split out. - friend class SQLStatement; friend class SQLTransaction; + friend class SQLTransactionBackend; }; } // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) - -#endif // Database_h diff --git a/Source/WebCore/Modules/webdatabase/Database.idl b/Source/WebCore/Modules/webdatabase/Database.idl index 463fa406f..5fa67f486 100644 --- a/Source/WebCore/Modules/webdatabase/Database.idl +++ b/Source/WebCore/Modules/webdatabase/Database.idl @@ -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. * @@ -27,13 +27,11 @@ */ [ - NoInterfaceObject, - Conditional=SQL_DATABASE, - JSNoStaticTables, + ImplementationLacksVTable ] interface Database { readonly attribute DOMString version; - void changeVersion(DOMString oldVersion, DOMString newVersion, optional SQLTransactionCallback callback, optional SQLTransactionErrorCallback errorCallback, optional VoidCallback successCallback); - void transaction(SQLTransactionCallback callback, optional SQLTransactionErrorCallback errorCallback, optional VoidCallback successCallback); - void readTransaction(SQLTransactionCallback callback, optional SQLTransactionErrorCallback errorCallback, optional VoidCallback successCallback); + void changeVersion(DOMString oldVersion, DOMString newVersion, optional SQLTransactionCallback? callback, optional SQLTransactionErrorCallback? errorCallback, optional VoidCallback? successCallback); + void transaction(SQLTransactionCallback callback, optional SQLTransactionErrorCallback? errorCallback, optional VoidCallback? successCallback); + void readTransaction(SQLTransactionCallback callback, optional SQLTransactionErrorCallback? errorCallback, optional VoidCallback? successCallback); }; diff --git a/Source/WebCore/Modules/webdatabase/DatabaseAuthorizer.cpp b/Source/WebCore/Modules/webdatabase/DatabaseAuthorizer.cpp index 13932fb01..f85f9207c 100644 --- a/Source/WebCore/Modules/webdatabase/DatabaseAuthorizer.cpp +++ b/Source/WebCore/Modules/webdatabase/DatabaseAuthorizer.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,14 +29,13 @@ #include "config.h" #include "DatabaseAuthorizer.h" -#include <wtf/PassRefPtr.h> #include <wtf/text/WTFString.h> namespace WebCore { -PassRefPtr<DatabaseAuthorizer> DatabaseAuthorizer::create(const String& databaseInfoTableName) +Ref<DatabaseAuthorizer> DatabaseAuthorizer::create(const String& databaseInfoTableName) { - return adoptRef(new DatabaseAuthorizer(databaseInfoTableName)); + return adoptRef(*new DatabaseAuthorizer(databaseInfoTableName)); } DatabaseAuthorizer::DatabaseAuthorizer(const String& databaseInfoTableName) @@ -283,29 +282,14 @@ int DatabaseAuthorizer::dropTempView(const String&) return SQLAuthAllow; } -int DatabaseAuthorizer::createVTable(const String& tableName, const String& moduleName) +int DatabaseAuthorizer::createVTable(const String&, const String&) { - if (!allowWrite()) - return SQLAuthDeny; - - // Allow only the FTS3 extension - if (!equalIgnoringCase(moduleName, "fts3")) - return SQLAuthDeny; - - m_lastActionChangedDatabase = true; - return denyBasedOnTableName(tableName); + return SQLAuthDeny; } -int DatabaseAuthorizer::dropVTable(const String& tableName, const String& moduleName) +int DatabaseAuthorizer::dropVTable(const String&, const String&) { - if (!allowWrite()) - return SQLAuthDeny; - - // Allow only the FTS3 extension - if (!equalIgnoringCase(moduleName, "fts3")) - return SQLAuthDeny; - - return updateDeletesBasedOnTableName(tableName); + return SQLAuthDeny; } int DatabaseAuthorizer::allowDelete(const String& tableName) @@ -344,7 +328,7 @@ int DatabaseAuthorizer::allowRead(const String& tableName, const String&) { if (m_permissions & NoAccessMask && m_securityEnabled) return SQLAuthDeny; - + return denyBasedOnTableName(tableName); } @@ -396,11 +380,6 @@ bool DatabaseAuthorizer::allowWrite() return !(m_securityEnabled && (m_permissions & ReadOnlyMask || m_permissions & NoAccessMask)); } -void DatabaseAuthorizer::setReadOnly() -{ - m_permissions |= ReadOnlyMask; -} - void DatabaseAuthorizer::setPermissions(int permissions) { m_permissions = permissions; @@ -412,12 +391,14 @@ int DatabaseAuthorizer::denyBasedOnTableName(const String& tableName) const return SQLAuthAllow; // Sadly, normal creates and drops end up affecting sqlite_master in an authorizer callback, so - // it will be tough to enforce all of the following policies - //if (equalIgnoringCase(tableName, "sqlite_master") || equalIgnoringCase(tableName, "sqlite_temp_master") || - // equalIgnoringCase(tableName, "sqlite_sequence") || equalIgnoringCase(tableName, Database::databaseInfoTableName())) - // return SQLAuthDeny; - - if (equalIgnoringCase(tableName, m_databaseInfoTableName)) + // it will be tough to enforce all of the following policies. + // if (equalIgnoringASCIICase(tableName, "sqlite_master") + // || equalIgnoringASCIICase(tableName, "sqlite_temp_master") + // || equalIgnoringASCIICase(tableName, "sqlite_sequence") + // || equalIgnoringASCIICase(tableName, Database::databaseInfoTableName())) + // return SQLAuthDeny; + + if (equalIgnoringASCIICase(tableName, m_databaseInfoTableName)) return SQLAuthDeny; return SQLAuthAllow; diff --git a/Source/WebCore/Modules/webdatabase/DatabaseAuthorizer.h b/Source/WebCore/Modules/webdatabase/DatabaseAuthorizer.h index 5625f039d..72d2b6788 100644 --- a/Source/WebCore/Modules/webdatabase/DatabaseAuthorizer.h +++ b/Source/WebCore/Modules/webdatabase/DatabaseAuthorizer.h @@ -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. * @@ -25,8 +25,8 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DatabaseAuthorizer_h -#define DatabaseAuthorizer_h + +#pragma once #include <wtf/Forward.h> #include <wtf/HashSet.h> @@ -49,7 +49,7 @@ public: NoAccessMask = 1 << 2 }; - static PassRefPtr<DatabaseAuthorizer> create(const String& databaseInfoTableName); + static Ref<DatabaseAuthorizer> create(const String& databaseInfoTableName); int createTable(const String& tableName); int createTempTable(const String& tableName); @@ -93,7 +93,6 @@ public: void disable(); void enable(); - void setReadOnly(); void setPermissions(int permissions); void reset(); @@ -118,9 +117,7 @@ private: const String m_databaseInfoTableName; - HashSet<String, CaseFoldingHash> m_whitelistedFunctions; + HashSet<String, ASCIICaseInsensitiveHash> m_whitelistedFunctions; }; } // namespace WebCore - -#endif // DatabaseAuthorizer_h diff --git a/Source/WebCore/Modules/webdatabase/DatabaseBackend.cpp b/Source/WebCore/Modules/webdatabase/DatabaseBackend.cpp deleted file mode 100644 index 495e86a48..000000000 --- a/Source/WebCore/Modules/webdatabase/DatabaseBackend.cpp +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 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. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * 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. - */ - -#include "config.h" -#include "DatabaseBackend.h" - -#if ENABLE(SQL_DATABASE) - -#include "ChangeVersionData.h" -#include "ChangeVersionWrapper.h" -#include "DatabaseBackendContext.h" -#include "DatabaseTask.h" -#include "DatabaseThread.h" -#include "DatabaseTracker.h" -#include "Logging.h" -#include "SQLTransaction.h" -#include "SQLTransactionBackend.h" -#include "SQLTransactionClient.h" -#include "SQLTransactionCoordinator.h" - -namespace WebCore { - -DatabaseBackend::DatabaseBackend(PassRefPtr<DatabaseBackendContext> databaseContext, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize) - : DatabaseBackendBase(databaseContext, name, expectedVersion, displayName, estimatedSize, DatabaseType::Async) - , m_transactionInProgress(false) - , m_isTransactionQueueEnabled(true) -{ -} - -bool DatabaseBackend::openAndVerifyVersion(bool setVersionInNewDatabase, DatabaseError& error, String& errorMessage) -{ - DatabaseTaskSynchronizer synchronizer; - if (!databaseContext()->databaseThread() || databaseContext()->databaseThread()->terminationRequested(&synchronizer)) - return false; - - bool success = false; - auto task = DatabaseOpenTask::create(this, setVersionInNewDatabase, &synchronizer, error, errorMessage, success); - databaseContext()->databaseThread()->scheduleImmediateTask(std::move(task)); - synchronizer.waitForTaskCompletion(); - - return success; -} - -bool DatabaseBackend::performOpenAndVerify(bool setVersionInNewDatabase, DatabaseError& error, String& errorMessage) -{ - if (DatabaseBackendBase::performOpenAndVerify(setVersionInNewDatabase, error, errorMessage)) { - if (databaseContext()->databaseThread()) - databaseContext()->databaseThread()->recordDatabaseOpen(this); - - return true; - } - - return false; -} - -void DatabaseBackend::close() -{ - ASSERT(databaseContext()->databaseThread()); - ASSERT(currentThread() == databaseContext()->databaseThread()->getThreadID()); - - { - MutexLocker locker(m_transactionInProgressMutex); - - // Clean up transactions that have not been scheduled yet: - // Transaction phase 1 cleanup. See comment on "What happens if a - // transaction is interrupted?" at the top of SQLTransactionBackend.cpp. - RefPtr<SQLTransactionBackend> transaction; - while (!m_transactionQueue.isEmpty()) { - transaction = m_transactionQueue.takeFirst(); - transaction->notifyDatabaseThreadIsShuttingDown(); - } - - m_isTransactionQueueEnabled = false; - m_transactionInProgress = false; - } - - closeDatabase(); - - // DatabaseThread keeps databases alive by referencing them in its - // m_openDatabaseSet. DatabaseThread::recordDatabaseClose() will remove - // this database from that set (which effectively deref's it). We hold on - // to it with a local pointer here for a liitle longer, so that we can - // unschedule any DatabaseTasks that refer to it before the database gets - // deleted. - Ref<DatabaseBackend> protect(*this); - databaseContext()->databaseThread()->recordDatabaseClosed(this); - databaseContext()->databaseThread()->unscheduleDatabaseTasks(this); -} - -PassRefPtr<SQLTransactionBackend> DatabaseBackend::runTransaction(PassRefPtr<SQLTransaction> transaction, - bool readOnly, const ChangeVersionData* data) -{ - MutexLocker locker(m_transactionInProgressMutex); - if (!m_isTransactionQueueEnabled) - return 0; - - RefPtr<SQLTransactionWrapper> wrapper; - if (data) - wrapper = ChangeVersionWrapper::create(data->oldVersion(), data->newVersion()); - - RefPtr<SQLTransactionBackend> transactionBackend = SQLTransactionBackend::create(this, transaction, wrapper, readOnly); - m_transactionQueue.append(transactionBackend); - if (!m_transactionInProgress) - scheduleTransaction(); - - return transactionBackend; -} - -void DatabaseBackend::inProgressTransactionCompleted() -{ - MutexLocker locker(m_transactionInProgressMutex); - m_transactionInProgress = false; - scheduleTransaction(); -} - -void DatabaseBackend::scheduleTransaction() -{ - ASSERT(!m_transactionInProgressMutex.tryLock()); // Locked by caller. - RefPtr<SQLTransactionBackend> transaction; - - if (m_isTransactionQueueEnabled && !m_transactionQueue.isEmpty()) - transaction = m_transactionQueue.takeFirst(); - - if (transaction && databaseContext()->databaseThread()) { - auto task = DatabaseTransactionTask::create(transaction); - LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for transaction %p\n", task.get(), task->transaction()); - m_transactionInProgress = true; - databaseContext()->databaseThread()->scheduleTask(std::move(task)); - } else - m_transactionInProgress = false; -} - -void DatabaseBackend::scheduleTransactionStep(SQLTransactionBackend* transaction) -{ - if (!databaseContext()->databaseThread()) - return; - - auto task = DatabaseTransactionTask::create(transaction); - LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for the transaction step\n", task.get()); - databaseContext()->databaseThread()->scheduleTask(std::move(task)); -} - -SQLTransactionClient* DatabaseBackend::transactionClient() const -{ - return databaseContext()->databaseThread()->transactionClient(); -} - -SQLTransactionCoordinator* DatabaseBackend::transactionCoordinator() const -{ - return databaseContext()->databaseThread()->transactionCoordinator(); -} - -} // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) diff --git a/Source/WebCore/Modules/webdatabase/DatabaseBackend.h b/Source/WebCore/Modules/webdatabase/DatabaseBackend.h deleted file mode 100644 index 8506e731f..000000000 --- a/Source/WebCore/Modules/webdatabase/DatabaseBackend.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 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. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * 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. - */ - -#ifndef DatabaseBackend_h -#define DatabaseBackend_h - -#if ENABLE(SQL_DATABASE) - -#include "DatabaseBackendBase.h" -#include <wtf/Deque.h> -#include <wtf/text/WTFString.h> - -namespace WebCore { - -class ChangeVersionData; -class Database; -class DatabaseServer; -class SQLTransaction; -class SQLTransactionBackend; -class SQLTransactionClient; -class SQLTransactionCoordinator; - -// FIXME: This implementation of DatabaseBackend is only a place holder -// for the split out of the Database backend to be done later. This -// place holder is needed to allow other code that need to reference the -// DatabaseBackend to do so before the proper backend split is -// available. This should be replaced with the actual implementation later. - -class DatabaseBackend : public DatabaseBackendBase { -public: - DatabaseBackend(PassRefPtr<DatabaseBackendContext>, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize); - - virtual bool openAndVerifyVersion(bool setVersionInNewDatabase, DatabaseError&, String& errorMessage); - void close(); - - PassRefPtr<SQLTransactionBackend> runTransaction(PassRefPtr<SQLTransaction>, bool readOnly, const ChangeVersionData*); - void scheduleTransactionStep(SQLTransactionBackend*); - void inProgressTransactionCompleted(); - - SQLTransactionClient* transactionClient() const; - SQLTransactionCoordinator* transactionCoordinator() const; - -private: - class DatabaseOpenTask; - class DatabaseCloseTask; - class DatabaseTransactionTask; - class DatabaseTableNamesTask; - - virtual bool performOpenAndVerify(bool setVersionInNewDatabase, DatabaseError&, String& errorMessage); - - void scheduleTransaction(); - - Deque<RefPtr<SQLTransactionBackend>> m_transactionQueue; - Mutex m_transactionInProgressMutex; - bool m_transactionInProgress; - bool m_isTransactionQueueEnabled; - - friend class Database; -}; - -} // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) - -#endif // DatabaseBackend_h diff --git a/Source/WebCore/Modules/webdatabase/DatabaseBackendBase.cpp b/Source/WebCore/Modules/webdatabase/DatabaseBackendBase.cpp deleted file mode 100644 index ea3e6112c..000000000 --- a/Source/WebCore/Modules/webdatabase/DatabaseBackendBase.cpp +++ /dev/null @@ -1,626 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 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 - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR 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. - */ - -#include "config.h" -#include "DatabaseBackendBase.h" - -#if ENABLE(SQL_DATABASE) - -#include "DatabaseAuthorizer.h" -#include "DatabaseBackendContext.h" -#include "DatabaseBase.h" -#include "DatabaseContext.h" -#include "DatabaseManager.h" -#include "DatabaseTracker.h" -#include "ExceptionCode.h" -#include "Logging.h" -#include "SQLiteDatabaseTracker.h" -#include "SQLiteStatement.h" -#include "SQLiteTransaction.h" -#include "SecurityOrigin.h" -#include <wtf/HashMap.h> -#include <wtf/HashSet.h> -#include <wtf/NeverDestroyed.h> -#include <wtf/PassRefPtr.h> -#include <wtf/RefPtr.h> -#include <wtf/StdLibExtras.h> -#include <wtf/text/CString.h> -#include <wtf/text/StringHash.h> - -// Registering "opened" databases with the DatabaseTracker -// ======================================================= -// The DatabaseTracker maintains a list of databases that have been -// "opened" so that the client can call interrupt or delete on every database -// associated with a DatabaseBackendContext. -// -// We will only call DatabaseTracker::addOpenDatabase() to add the database -// to the tracker as opened when we've succeeded in opening the database, -// and will set m_opened to true. Similarly, we only call -// DatabaseTracker::removeOpenDatabase() to remove the database from the -// tracker when we set m_opened to false in closeDatabase(). This sets up -// a simple symmetry between open and close operations, and a direct -// correlation to adding and removing databases from the tracker's list, -// thus ensuring that we have a correct list for the interrupt and -// delete operations to work on. -// -// The only databases instances not tracked by the tracker's open database -// list are the ones that have not been added yet, or the ones that we -// attempted an open on but failed to. Such instances only exist in the -// DatabaseServer's factory methods for creating database backends. -// -// The factory methods will either call openAndVerifyVersion() or -// performOpenAndVerify(). These methods will add the newly instantiated -// database backend if they succeed in opening the requested database. -// In the case of failure to open the database, the factory methods will -// simply discard the newly instantiated database backend when they return. -// The ref counting mechanims will automatically destruct the un-added -// (and un-returned) databases instances. - -namespace WebCore { - -static const char versionKey[] = "WebKitDatabaseVersionKey"; -static const char unqualifiedInfoTableName[] = "__WebKitDatabaseInfoTable__"; - -const char* DatabaseBackendBase::databaseInfoTableName() -{ - return unqualifiedInfoTableName; -} - -static const char* fullyQualifiedInfoTableName() -{ - static const char qualifier[] = "main."; - static char qualifiedName[sizeof(qualifier) + sizeof(unqualifiedInfoTableName) - 1]; - - static std::once_flag onceFlag; - std::call_once(onceFlag, []{ - strcpy(qualifiedName, qualifier); - strcpy(qualifiedName + strlen(qualifier), unqualifiedInfoTableName); - }); - - return qualifiedName; -} - -static String formatErrorMessage(const char* message, int sqliteErrorCode, const char* sqliteErrorMessage) -{ - return String::format("%s (%d %s)", message, sqliteErrorCode, sqliteErrorMessage); -} - -static bool retrieveTextResultFromDatabase(SQLiteDatabase& db, const String& query, String& resultString) -{ - SQLiteStatement statement(db, query); - int result = statement.prepare(); - - if (result != SQLResultOk) { - LOG_ERROR("Error (%i) preparing statement to read text result from database (%s)", result, query.ascii().data()); - return false; - } - - result = statement.step(); - if (result == SQLResultRow) { - resultString = statement.getColumnText(0); - return true; - } - if (result == SQLResultDone) { - resultString = String(); - return true; - } - - LOG_ERROR("Error (%i) reading text result from database (%s)", result, query.ascii().data()); - return false; -} - -static bool setTextValueInDatabase(SQLiteDatabase& db, const String& query, const String& value) -{ - SQLiteStatement statement(db, query); - int result = statement.prepare(); - - if (result != SQLResultOk) { - LOG_ERROR("Failed to prepare statement to set value in database (%s)", query.ascii().data()); - return false; - } - - statement.bindText(1, value); - - result = statement.step(); - if (result != SQLResultDone) { - LOG_ERROR("Failed to step statement to set value in database (%s)", query.ascii().data()); - return false; - } - - return true; -} - -// FIXME: move all guid-related functions to a DatabaseVersionTracker class. -static std::mutex& guidMutex() -{ - static std::once_flag onceFlag; - static std::mutex* mutex; - - std::call_once(onceFlag, []{ - mutex = std::make_unique<std::mutex>().release(); - }); - - return *mutex; -} - -typedef HashMap<DatabaseGuid, String> GuidVersionMap; -static GuidVersionMap& guidToVersionMap() -{ - // Ensure the the mutex is locked. - ASSERT(!guidMutex().try_lock()); - - static NeverDestroyed<GuidVersionMap> map; - return map; -} - -// NOTE: Caller must lock guidMutex(). -static inline void updateGuidVersionMap(DatabaseGuid guid, String newVersion) -{ - // Ensure the the mutex is locked. - ASSERT(!guidMutex().try_lock()); - - // Note: It is not safe to put an empty string into the guidToVersionMap() map. - // That's because the map is cross-thread, but empty strings are per-thread. - // The copy() function makes a version of the string you can use on the current - // thread, but we need a string we can keep in a cross-thread data structure. - // FIXME: This is a quite-awkward restriction to have to program with. - - // Map null string to empty string (see comment above). - guidToVersionMap().set(guid, newVersion.isEmpty() ? String() : newVersion.isolatedCopy()); -} - -typedef HashMap<DatabaseGuid, std::unique_ptr<HashSet<DatabaseBackendBase*>>> GuidDatabaseMap; - -static GuidDatabaseMap& guidToDatabaseMap() -{ - // Ensure the the mutex is locked. - ASSERT(!guidMutex().try_lock()); - - static NeverDestroyed<GuidDatabaseMap> map; - return map; -} - -static DatabaseGuid guidForOriginAndName(const String& origin, const String& name) -{ - // Ensure the the mutex is locked. - ASSERT(!guidMutex().try_lock()); - - String stringID = origin + "/" + name; - - typedef HashMap<String, int> IDGuidMap; - static NeverDestroyed<HashMap<String, int>> map; - DatabaseGuid guid = map.get().get(stringID); - if (!guid) { - static int currentNewGUID = 1; - guid = currentNewGUID++; - map.get().set(stringID, guid); - } - - return guid; -} - -#if !LOG_DISABLED || !ERROR_DISABLED -String DatabaseBackendBase::databaseDebugName() const -{ - return m_contextThreadSecurityOrigin->toString() + "::" + m_name; -} -#endif - -DatabaseBackendBase::DatabaseBackendBase(PassRefPtr<DatabaseBackendContext> databaseContext, const String& name, - const String& expectedVersion, const String& displayName, unsigned long estimatedSize, DatabaseType databaseType) - : m_databaseContext(databaseContext) - , m_name(name.isolatedCopy()) - , m_expectedVersion(expectedVersion.isolatedCopy()) - , m_displayName(displayName.isolatedCopy()) - , m_estimatedSize(estimatedSize) - , m_opened(false) - , m_new(false) - , m_isSyncDatabase(databaseType == DatabaseType::Sync) -{ - m_contextThreadSecurityOrigin = m_databaseContext->securityOrigin()->isolatedCopy(); - - m_databaseAuthorizer = DatabaseAuthorizer::create(unqualifiedInfoTableName); - - if (m_name.isNull()) - m_name = emptyString(); - - { - std::lock_guard<std::mutex> locker(guidMutex()); - - m_guid = guidForOriginAndName(securityOrigin()->toString(), name); - std::unique_ptr<HashSet<DatabaseBackendBase*>>& hashSet = guidToDatabaseMap().add(m_guid, nullptr).iterator->value; - if (!hashSet) - hashSet = std::make_unique<HashSet<DatabaseBackendBase*>>(); - hashSet->add(this); - } - - m_filename = DatabaseManager::manager().fullPathForDatabase(securityOrigin(), m_name); -} - -DatabaseBackendBase::~DatabaseBackendBase() -{ - // SQLite is "multi-thread safe", but each database handle can only be used - // on a single thread at a time. - // - // For DatabaseBackend, we open the SQLite database on the DatabaseThread, - // and hence we should also close it on that same thread. This means that the - // SQLite database need to be closed by another mechanism (see - // DatabaseContext::stopDatabases()). By the time we get here, the SQLite - // database should have already been closed. - - ASSERT(!m_opened); -} - -void DatabaseBackendBase::closeDatabase() -{ - if (!m_opened) - return; - - m_sqliteDatabase.close(); - m_opened = false; - // See comment at the top this file regarding calling removeOpenDatabase(). - DatabaseTracker::tracker().removeOpenDatabase(this); - { - std::lock_guard<std::mutex> locker(guidMutex()); - - auto it = guidToDatabaseMap().find(m_guid); - ASSERT(it != guidToDatabaseMap().end()); - ASSERT(it->value); - ASSERT(it->value->contains(this)); - it->value->remove(this); - if (it->value->isEmpty()) { - guidToDatabaseMap().remove(it); - guidToVersionMap().remove(m_guid); - } - } -} - -String DatabaseBackendBase::version() const -{ - // Note: In multi-process browsers the cached value may be accurate, but we cannot read the - // actual version from the database without potentially inducing a deadlock. - // FIXME: Add an async version getter to the DatabaseAPI. - return getCachedVersion(); -} - -class DoneCreatingDatabaseOnExitCaller { -public: - DoneCreatingDatabaseOnExitCaller(DatabaseBackendBase* database) - : m_database(database) - , m_openSucceeded(false) - { - } - ~DoneCreatingDatabaseOnExitCaller() - { - DatabaseTracker::tracker().doneCreatingDatabase(m_database); - } - - void setOpenSucceeded() { m_openSucceeded = true; } - -private: - DatabaseBackendBase* m_database; - bool m_openSucceeded; -}; - -bool DatabaseBackendBase::performOpenAndVerify(bool shouldSetVersionInNewDatabase, DatabaseError& error, String& errorMessage) -{ - DoneCreatingDatabaseOnExitCaller onExitCaller(this); - ASSERT(errorMessage.isEmpty()); - ASSERT(error == DatabaseError::None); // Better not have any errors already. - error = DatabaseError::InvalidDatabaseState; // Presumed failure. We'll clear it if we succeed below. - - const int maxSqliteBusyWaitTime = 30000; - -#if PLATFORM(IOS) - { - // Make sure we wait till the background removal of the empty database files finished before trying to open any database. - MutexLocker locker(DatabaseTracker::openDatabaseMutex()); - } -#endif - - SQLiteTransactionInProgressAutoCounter transactionCounter; - - if (!m_sqliteDatabase.open(m_filename, true)) { - errorMessage = formatErrorMessage("unable to open database", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); - return false; - } - if (!m_sqliteDatabase.turnOnIncrementalAutoVacuum()) - LOG_ERROR("Unable to turn on incremental auto-vacuum (%d %s)", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); - - m_sqliteDatabase.setBusyTimeout(maxSqliteBusyWaitTime); - - String currentVersion; - { - std::lock_guard<std::mutex> locker(guidMutex()); - - auto entry = guidToVersionMap().find(m_guid); - if (entry != guidToVersionMap().end()) { - // Map null string to empty string (see updateGuidVersionMap()). - currentVersion = entry->value.isNull() ? emptyString() : entry->value.isolatedCopy(); - LOG(StorageAPI, "Current cached version for guid %i is %s", m_guid, currentVersion.ascii().data()); - } else { - LOG(StorageAPI, "No cached version for guid %i", m_guid); - - SQLiteTransaction transaction(m_sqliteDatabase); - transaction.begin(); - if (!transaction.inProgress()) { - errorMessage = formatErrorMessage("unable to open database, failed to start transaction", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); - m_sqliteDatabase.close(); - return false; - } - - String tableName(unqualifiedInfoTableName); - if (!m_sqliteDatabase.tableExists(tableName)) { - m_new = true; - - if (!m_sqliteDatabase.executeCommand("CREATE TABLE " + tableName + " (key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,value TEXT NOT NULL ON CONFLICT FAIL);")) { - errorMessage = formatErrorMessage("unable to open database, failed to create 'info' table", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); - transaction.rollback(); - m_sqliteDatabase.close(); - return false; - } - } else if (!getVersionFromDatabase(currentVersion, false)) { - errorMessage = formatErrorMessage("unable to open database, failed to read current version", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); - transaction.rollback(); - m_sqliteDatabase.close(); - return false; - } - - if (currentVersion.length()) { - LOG(StorageAPI, "Retrieved current version %s from database %s", currentVersion.ascii().data(), databaseDebugName().ascii().data()); - } else if (!m_new || shouldSetVersionInNewDatabase) { - LOG(StorageAPI, "Setting version %s in database %s that was just created", m_expectedVersion.ascii().data(), databaseDebugName().ascii().data()); - if (!setVersionInDatabase(m_expectedVersion, false)) { - errorMessage = formatErrorMessage("unable to open database, failed to write current version", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); - transaction.rollback(); - m_sqliteDatabase.close(); - return false; - } - currentVersion = m_expectedVersion; - } - updateGuidVersionMap(m_guid, currentVersion); - transaction.commit(); - } - } - - if (currentVersion.isNull()) { - LOG(StorageAPI, "Database %s does not have its version set", databaseDebugName().ascii().data()); - currentVersion = ""; - } - - // If the expected version isn't the empty string, ensure that the current database version we have matches that version. Otherwise, set an exception. - // If the expected version is the empty string, then we always return with whatever version of the database we have. - if ((!m_new || shouldSetVersionInNewDatabase) && m_expectedVersion.length() && m_expectedVersion != currentVersion) { - errorMessage = "unable to open database, version mismatch, '" + m_expectedVersion + "' does not match the currentVersion of '" + currentVersion + "'"; - m_sqliteDatabase.close(); - return false; - } - - ASSERT(m_databaseAuthorizer); - m_sqliteDatabase.setAuthorizer(m_databaseAuthorizer); - - // See comment at the top this file regarding calling addOpenDatabase(). - DatabaseTracker::tracker().addOpenDatabase(this); - m_opened = true; - - // Declare success: - error = DatabaseError::None; // Clear the presumed error from above. - onExitCaller.setOpenSucceeded(); - - if (m_new && !shouldSetVersionInNewDatabase) - m_expectedVersion = ""; // The caller provided a creationCallback which will set the expected version. - return true; -} - -SecurityOrigin* DatabaseBackendBase::securityOrigin() const -{ - return m_contextThreadSecurityOrigin.get(); -} - -String DatabaseBackendBase::stringIdentifier() const -{ - // Return a deep copy for ref counting thread safety - return m_name.isolatedCopy(); -} - -String DatabaseBackendBase::displayName() const -{ - // Return a deep copy for ref counting thread safety - return m_displayName.isolatedCopy(); -} - -unsigned long DatabaseBackendBase::estimatedSize() const -{ - return m_estimatedSize; -} - -String DatabaseBackendBase::fileName() const -{ - // Return a deep copy for ref counting thread safety - return m_filename.isolatedCopy(); -} - -DatabaseDetails DatabaseBackendBase::details() const -{ - // This code path is only used for database quota delegate calls, so file dates are irrelevant and left uninitialized. - return DatabaseDetails(stringIdentifier(), displayName(), estimatedSize(), 0, 0, 0); -} - -bool DatabaseBackendBase::getVersionFromDatabase(String& version, bool shouldCacheVersion) -{ - String query(String("SELECT value FROM ") + fullyQualifiedInfoTableName() + " WHERE key = '" + versionKey + "';"); - - m_databaseAuthorizer->disable(); - - bool result = retrieveTextResultFromDatabase(m_sqliteDatabase, query, version); - if (result) { - if (shouldCacheVersion) - setCachedVersion(version); - } else - LOG_ERROR("Failed to retrieve version from database %s", databaseDebugName().ascii().data()); - - m_databaseAuthorizer->enable(); - - return result; -} - -bool DatabaseBackendBase::setVersionInDatabase(const String& version, bool shouldCacheVersion) -{ - // The INSERT will replace an existing entry for the database with the new version number, due to the UNIQUE ON CONFLICT REPLACE - // clause in the CREATE statement (see Database::performOpenAndVerify()). - String query(String("INSERT INTO ") + fullyQualifiedInfoTableName() + " (key, value) VALUES ('" + versionKey + "', ?);"); - - m_databaseAuthorizer->disable(); - - bool result = setTextValueInDatabase(m_sqliteDatabase, query, version); - if (result) { - if (shouldCacheVersion) - setCachedVersion(version); - } else - LOG_ERROR("Failed to set version %s in database (%s)", version.ascii().data(), query.ascii().data()); - - m_databaseAuthorizer->enable(); - - return result; -} - -void DatabaseBackendBase::setExpectedVersion(const String& version) -{ - m_expectedVersion = version.isolatedCopy(); -} - -String DatabaseBackendBase::getCachedVersion() const -{ - std::lock_guard<std::mutex> locker(guidMutex()); - - return guidToVersionMap().get(m_guid).isolatedCopy(); -} - -void DatabaseBackendBase::setCachedVersion(const String& actualVersion) -{ - // Update the in memory database version map. - std::lock_guard<std::mutex> locker(guidMutex()); - - updateGuidVersionMap(m_guid, actualVersion); -} - -bool DatabaseBackendBase::getActualVersionForTransaction(String &actualVersion) -{ - ASSERT(m_sqliteDatabase.transactionInProgress()); - // Note: In multi-process browsers the cached value may be inaccurate. - // So we retrieve the value from the database and update the cached value here. - return getVersionFromDatabase(actualVersion, true); -} - -void DatabaseBackendBase::disableAuthorizer() -{ - ASSERT(m_databaseAuthorizer); - m_databaseAuthorizer->disable(); -} - -void DatabaseBackendBase::enableAuthorizer() -{ - ASSERT(m_databaseAuthorizer); - m_databaseAuthorizer->enable(); -} - -void DatabaseBackendBase::setAuthorizerReadOnly() -{ - ASSERT(m_databaseAuthorizer); - m_databaseAuthorizer->setReadOnly(); -} - -void DatabaseBackendBase::setAuthorizerPermissions(int permissions) -{ - ASSERT(m_databaseAuthorizer); - m_databaseAuthorizer->setPermissions(permissions); -} - -bool DatabaseBackendBase::lastActionChangedDatabase() -{ - ASSERT(m_databaseAuthorizer); - return m_databaseAuthorizer->lastActionChangedDatabase(); -} - -bool DatabaseBackendBase::lastActionWasInsert() -{ - ASSERT(m_databaseAuthorizer); - return m_databaseAuthorizer->lastActionWasInsert(); -} - -void DatabaseBackendBase::resetDeletes() -{ - ASSERT(m_databaseAuthorizer); - m_databaseAuthorizer->resetDeletes(); -} - -bool DatabaseBackendBase::hadDeletes() -{ - ASSERT(m_databaseAuthorizer); - return m_databaseAuthorizer->hadDeletes(); -} - -void DatabaseBackendBase::resetAuthorizer() -{ - if (m_databaseAuthorizer) - m_databaseAuthorizer->reset(); -} - -unsigned long long DatabaseBackendBase::maximumSize() const -{ - return DatabaseTracker::tracker().getMaxSizeForDatabase(this); -} - -void DatabaseBackendBase::incrementalVacuumIfNeeded() -{ - SQLiteTransactionInProgressAutoCounter transactionCounter; - - int64_t freeSpaceSize = m_sqliteDatabase.freeSpaceSize(); - int64_t totalSize = m_sqliteDatabase.totalSize(); - if (totalSize <= 10 * freeSpaceSize) { - int result = m_sqliteDatabase.runIncrementalVacuumCommand(); - if (result != SQLResultOk) - m_frontend->logErrorMessage(formatErrorMessage("error vacuuming database", result, m_sqliteDatabase.lastErrorMsg())); - } -} - -void DatabaseBackendBase::interrupt() -{ - m_sqliteDatabase.interrupt(); -} - -bool DatabaseBackendBase::isInterrupted() -{ - MutexLocker locker(m_sqliteDatabase.databaseMutex()); - return m_sqliteDatabase.isInterrupted(); -} - -} // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) diff --git a/Source/WebCore/Modules/webdatabase/DatabaseBackendBase.h b/Source/WebCore/Modules/webdatabase/DatabaseBackendBase.h deleted file mode 100644 index 113737cc0..000000000 --- a/Source/WebCore/Modules/webdatabase/DatabaseBackendBase.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 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 - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR 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. - */ - -#ifndef DatabaseBackendBase_h -#define DatabaseBackendBase_h - -#if ENABLE(SQL_DATABASE) - -#include "DatabaseBasicTypes.h" -#include "DatabaseDetails.h" -#include "DatabaseError.h" -#include "SQLiteDatabase.h" -#include <wtf/Forward.h> -#include <wtf/RefPtr.h> -#include <wtf/ThreadSafeRefCounted.h> -#include <wtf/text/WTFString.h> - -namespace WebCore { - -class DatabaseAuthorizer; -class DatabaseBackendContext; -class DatabaseBase; -class SecurityOrigin; - -class DatabaseBackendBase : public ThreadSafeRefCounted<DatabaseBackendBase> { -public: - virtual ~DatabaseBackendBase(); - - virtual String version() const; - - bool opened() const { return m_opened; } - bool isNew() const { return m_new; } - bool isSyncDatabase() const { return m_isSyncDatabase; } - - virtual SecurityOrigin* securityOrigin() const; - virtual String stringIdentifier() const; - virtual String displayName() const; - virtual unsigned long estimatedSize() const; - virtual String fileName() const; - virtual DatabaseDetails details() const; - SQLiteDatabase& sqliteDatabase() { return m_sqliteDatabase; } - - unsigned long long maximumSize() const; - void incrementalVacuumIfNeeded(); - void interrupt(); - bool isInterrupted(); - - void disableAuthorizer(); - void enableAuthorizer(); - void setAuthorizerReadOnly(); - void setAuthorizerPermissions(int permissions); - bool lastActionChangedDatabase(); - bool lastActionWasInsert(); - void resetDeletes(); - bool hadDeletes(); - void resetAuthorizer(); - - virtual void markAsDeletedAndClose() = 0; - virtual void closeImmediately() = 0; - - DatabaseBackendContext* databaseContext() const { return m_databaseContext.get(); } - void setFrontend(DatabaseBase* frontend) { m_frontend = frontend; } - -protected: - friend class ChangeVersionWrapper; - friend class SQLStatementBackend; - friend class SQLStatementSync; - friend class SQLTransactionBackend; - friend class SQLTransactionBackendSync; - - DatabaseBackendBase(PassRefPtr<DatabaseBackendContext>, const String& name, const String& expectedVersion, - const String& displayName, unsigned long estimatedSize, DatabaseType); - - void closeDatabase(); - - virtual bool openAndVerifyVersion(bool setVersionInNewDatabase, DatabaseError&, String& errorMessage) = 0; - virtual bool performOpenAndVerify(bool shouldSetVersionInNewDatabase, DatabaseError&, String& errorMessage); - - bool getVersionFromDatabase(String& version, bool shouldCacheVersion = true); - bool setVersionInDatabase(const String& version, bool shouldCacheVersion = true); - void setExpectedVersion(const String&); - const String& expectedVersion() const { return m_expectedVersion; } - String getCachedVersion()const; - void setCachedVersion(const String&); - bool getActualVersionForTransaction(String& version); - - static const char* databaseInfoTableName(); - -#if !LOG_DISABLED || !ERROR_DISABLED - String databaseDebugName() const; -#endif - - RefPtr<SecurityOrigin> m_contextThreadSecurityOrigin; - RefPtr<DatabaseBackendContext> m_databaseContext; // Associated with m_scriptExecutionContext. - - String m_name; - String m_expectedVersion; - String m_displayName; - unsigned long m_estimatedSize; - String m_filename; - - DatabaseBase* m_frontend; - -private: - DatabaseGuid m_guid; - bool m_opened; - bool m_new; - const bool m_isSyncDatabase; - - SQLiteDatabase m_sqliteDatabase; - - RefPtr<DatabaseAuthorizer> m_databaseAuthorizer; - - friend class DatabaseServer; -}; - -} // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) - -#endif // DatabaseBackendBase_h diff --git a/Source/WebCore/Modules/webdatabase/DatabaseBackendContext.cpp b/Source/WebCore/Modules/webdatabase/DatabaseBackendContext.cpp deleted file mode 100644 index 8a19360fe..000000000 --- a/Source/WebCore/Modules/webdatabase/DatabaseBackendContext.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 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. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * 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. - */ - -#include "config.h" -#include "DatabaseBackendContext.h" - -#if ENABLE(SQL_DATABASE) - -#include "ScriptExecutionContext.h" - -namespace WebCore { - -DatabaseContext* DatabaseBackendContext::frontend() -{ - // FIXME: Currently, we're only simulating the frontend by return the - // backend context as its own the frontend. When we split the 2 apart, this - // create() function should be changed to return a cached m_frontend. - return static_cast<DatabaseContext*>(this); -} - -SecurityOrigin* DatabaseBackendContext::securityOrigin() const -{ - return m_scriptExecutionContext->securityOrigin(); -} - -bool DatabaseBackendContext::isContextThread() const -{ - return m_scriptExecutionContext->isContextThread(); -} - -} // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) diff --git a/Source/WebCore/Modules/webdatabase/DatabaseBackendContext.h b/Source/WebCore/Modules/webdatabase/DatabaseBackendContext.h deleted file mode 100644 index b3abef7b5..000000000 --- a/Source/WebCore/Modules/webdatabase/DatabaseBackendContext.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 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. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * 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. - */ - -#ifndef DatabaseBackendContext_h -#define DatabaseBackendContext_h - -#if ENABLE(SQL_DATABASE) - -#include "DatabaseContext.h" - -namespace WebCore { - -class ScriptExecutionContext; -class SecurityOrigin; - -// FIXME: This implementation of DatabaseBackendContext is only a place holder -// for the split out of the DatabaseContext backend to be done later. This -// place holder is needed to allow other code that need to reference the -// DatabaseBackendContext to do so before the proper backend split is -// available. This should be replaced with the actual implementation later. - -class DatabaseBackendContext : public DatabaseContext { -public: - DatabaseContext* frontend(); - - ScriptExecutionContext* scriptExecutionContext() const { return m_scriptExecutionContext; } - SecurityOrigin* securityOrigin() const; - - bool isContextThread() const; -}; - -} // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) - -#endif // DatabaseBackendContext_h diff --git a/Source/WebCore/Modules/webdatabase/DatabaseBackendSync.cpp b/Source/WebCore/Modules/webdatabase/DatabaseBackendSync.cpp deleted file mode 100644 index a29b483b9..000000000 --- a/Source/WebCore/Modules/webdatabase/DatabaseBackendSync.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 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. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * 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. - */ - -#include "config.h" -#include "DatabaseBackendSync.h" - -#if ENABLE(SQL_DATABASE) - -#include "DatabaseBackendContext.h" -#include "DatabaseTracker.h" - -namespace WebCore { - -DatabaseBackendSync::DatabaseBackendSync(PassRefPtr<DatabaseBackendContext> databaseContext, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize) - : DatabaseBackendBase(databaseContext, name, expectedVersion, displayName, estimatedSize, DatabaseType::Sync) -{ -} - -DatabaseBackendSync::~DatabaseBackendSync() -{ - // SQLite is "multi-thread safe", but each database handle can only be used - // on a single thread at a time. - // - // For DatabaseBackendSync, we open the SQLite database on the script context - // thread. And hence we should also close it on that same thread. This means - // that the SQLite database need to be closed here in the destructor. - - ASSERT(m_databaseContext->isContextThread()); - if (opened()) - closeDatabase(); -} - -bool DatabaseBackendSync::openAndVerifyVersion(bool setVersionInNewDatabase, DatabaseError& error, String& errorMessage) -{ - return performOpenAndVerify(setVersionInNewDatabase, error, errorMessage); -} - -} // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) diff --git a/Source/WebCore/Modules/webdatabase/DatabaseBackendSync.h b/Source/WebCore/Modules/webdatabase/DatabaseBackendSync.h deleted file mode 100644 index a976f7f18..000000000 --- a/Source/WebCore/Modules/webdatabase/DatabaseBackendSync.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 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. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * 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. - */ - -#ifndef DatabaseBackendSync_h -#define DatabaseBackendSync_h - -#if ENABLE(SQL_DATABASE) - -#include "DatabaseBackendBase.h" -#include <wtf/text/WTFString.h> - -namespace WebCore { - -class DatabaseServer; - -// FIXME: This implementation of DatabaseBackendSync is only a place holder -// for the split out of the DatabaseSync backend to be done later. This -// place holder is needed to allow other code that need to reference the -// DatabaseBackendSync to do so before the proper backend split is -// available. This should be replaced with the actual implementation later. - -class DatabaseBackendSync : public DatabaseBackendBase { -public: - virtual ~DatabaseBackendSync(); - - virtual bool openAndVerifyVersion(bool setVersionInNewDatabase, DatabaseError&, String& errorMessage); - -protected: - DatabaseBackendSync(PassRefPtr<DatabaseBackendContext>, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize); - - friend class DatabaseServer; -}; - -} // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) - -#endif // DatabaseBackendSync_h diff --git a/Source/WebCore/Modules/webdatabase/DatabaseBase.cpp b/Source/WebCore/Modules/webdatabase/DatabaseBase.cpp deleted file mode 100644 index af2377a73..000000000 --- a/Source/WebCore/Modules/webdatabase/DatabaseBase.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 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. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * 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. - */ - -#include "config.h" -#include "DatabaseBase.h" - -#if ENABLE(SQL_DATABASE) - -#include "ScriptExecutionContext.h" -#include <wtf/Assertions.h> - -namespace WebCore { - -DatabaseBase::DatabaseBase(ScriptExecutionContext* scriptExecutionContext) - : m_scriptExecutionContext(scriptExecutionContext) -{ - ASSERT(m_scriptExecutionContext->isContextThread()); -} - -ScriptExecutionContext* DatabaseBase::scriptExecutionContext() const -{ - return m_scriptExecutionContext.get(); -} - -void DatabaseBase::logErrorMessage(const String& message) -{ - m_scriptExecutionContext->addConsoleMessage(StorageMessageSource, ErrorMessageLevel, message); -} - -} // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) diff --git a/Source/WebCore/Modules/webdatabase/DatabaseBase.h b/Source/WebCore/Modules/webdatabase/DatabaseBase.h deleted file mode 100644 index 573bf14d6..000000000 --- a/Source/WebCore/Modules/webdatabase/DatabaseBase.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 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. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * 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. - */ - -#ifndef DatabaseBase_h -#define DatabaseBase_h - -#if ENABLE(SQL_DATABASE) - -#include <wtf/text/WTFString.h> - -namespace WebCore { - -class ScriptExecutionContext; - -class DatabaseBase { -public: - ScriptExecutionContext* scriptExecutionContext() const; - void logErrorMessage(const String& message); - -protected: - DatabaseBase(ScriptExecutionContext*); - - RefPtr<ScriptExecutionContext> m_scriptExecutionContext; -}; - -} // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) - -#endif // DatabaseBase_h diff --git a/Source/WebCore/Modules/webdatabase/DatabaseBasicTypes.h b/Source/WebCore/Modules/webdatabase/DatabaseBasicTypes.h deleted file mode 100644 index 8739f7085..000000000 --- a/Source/WebCore/Modules/webdatabase/DatabaseBasicTypes.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 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. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * 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. - */ - -#ifndef DatabaseBasicTypes_h -#define DatabaseBasicTypes_h - -#if ENABLE(SQL_DATABASE) - -namespace WebCore { - -typedef int DatabaseGuid; -typedef int ExceptionCode; - -enum class DatabaseType { - Async, - Sync -}; - -} // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) - -#endif // DatabaseBasicTypes_h diff --git a/Source/WebCore/Modules/webdatabase/DatabaseCallback.h b/Source/WebCore/Modules/webdatabase/DatabaseCallback.h index bfa7bc627..f7dc3a1f6 100644 --- a/Source/WebCore/Modules/webdatabase/DatabaseCallback.h +++ b/Source/WebCore/Modules/webdatabase/DatabaseCallback.h @@ -28,27 +28,18 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DatabaseCallback_h -#define DatabaseCallback_h - -#if ENABLE(SQL_DATABASE) +#pragma once #include <wtf/ThreadSafeRefCounted.h> namespace WebCore { class Database; -class DatabaseSync; class DatabaseCallback : public ThreadSafeRefCounted<DatabaseCallback> { public: virtual ~DatabaseCallback() { } virtual bool handleEvent(Database*) = 0; - virtual bool handleEvent(DatabaseSync*) = 0; }; -} - -#endif - -#endif // DatabaseCallback_h +} // namespace WebCore diff --git a/Source/WebCore/Modules/webdatabase/DatabaseCallback.idl b/Source/WebCore/Modules/webdatabase/DatabaseCallback.idl index f2aa31def..9e0a5de36 100644 --- a/Source/WebCore/Modules/webdatabase/DatabaseCallback.idl +++ b/Source/WebCore/Modules/webdatabase/DatabaseCallback.idl @@ -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. * @@ -26,9 +26,4 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -[ - Conditional=SQL_DATABASE, -] callback interface DatabaseCallback { - boolean handleEvent(Database database); - boolean handleEvent(DatabaseSync database); -}; +callback DatabaseCallback = void (Database database); diff --git a/Source/WebCore/Modules/webdatabase/DatabaseContext.cpp b/Source/WebCore/Modules/webdatabase/DatabaseContext.cpp index 752e29e48..3dcb56034 100644 --- a/Source/WebCore/Modules/webdatabase/DatabaseContext.cpp +++ b/Source/WebCore/Modules/webdatabase/DatabaseContext.cpp @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -28,12 +28,9 @@ #include "config.h" #include "DatabaseContext.h" -#if ENABLE(SQL_DATABASE) - #include "Chrome.h" #include "ChromeClient.h" #include "Database.h" -#include "DatabaseBackendContext.h" #include "DatabaseManager.h" #include "DatabaseTask.h" #include "DatabaseThread.h" @@ -42,7 +39,7 @@ #include "SchemeRegistry.h" #include "ScriptExecutionContext.h" #include "SecurityOrigin.h" -#include "Settings.h" +#include "SecurityOriginData.h" namespace WebCore { @@ -97,54 +94,32 @@ namespace WebCore { // DatabaseContext will outlive both regardless of which of the 2 destructs first. -DatabaseContext::DatabaseContext(ScriptExecutionContext* context) - : ActiveDOMObject(context) - , m_hasOpenDatabases(false) - , m_isRegistered(true) // will register on construction below. - , m_hasRequestedTermination(false) -#if PLATFORM(IOS) - , m_paused(false) -#endif //PLATFORM(IOS) +DatabaseContext::DatabaseContext(ScriptExecutionContext& context) + : ActiveDOMObject(&context) { // ActiveDOMObject expects this to be called to set internal flags. suspendIfNeeded(); - context->setDatabaseContext(this); - - // For debug accounting only. We must do this before we register the - // instance. The assertions assume this. - DatabaseManager::manager().didConstructDatabaseContext(); - - DatabaseManager::manager().registerDatabaseContext(this); + ASSERT(!context.databaseContext()); + context.setDatabaseContext(this); } DatabaseContext::~DatabaseContext() { stopDatabases(); ASSERT(!m_databaseThread || m_databaseThread->terminationRequested()); - - // For debug accounting only. We must call this last. The assertions assume - // this. - DatabaseManager::manager().didDestructDatabaseContext(); + ASSERT(!scriptExecutionContext() || !scriptExecutionContext()->databaseContext()); } -// This is called if the associated ScriptExecutionContext is destructing while +// This is called if the associated ScriptExecutionContext is destroyed while // we're still associated with it. That's our cue to disassociate and shutdown. -// To do this, we stop the database and let everything shutdown naturally -// because the database closing process may still make use of this context. +// To do this, we stop the database and let everything shut down naturally +// because the database closing process might still make use of this context. // It is not safe to just delete the context here. void DatabaseContext::contextDestroyed() { + ActiveDOMObject::contextDestroyed(); stopDatabases(); - - // Normally, willDestroyActiveDOMObject() is called in ~ActiveDOMObject(). - // However, we're here because the destructor hasn't been called, and the - // ScriptExecutionContext we're associated with is about to be destructed. - // So, go ahead an unregister self from the ActiveDOMObject list, and - // set m_scriptExecutionContext to 0 so that ~ActiveDOMObject() doesn't - // try to do so again. - m_scriptExecutionContext->willDestroyActiveDOMObject(this); - m_scriptExecutionContext = 0; } // stop() is from stopActiveDOMObjects() which indicates that the owner Frame @@ -155,18 +130,17 @@ void DatabaseContext::stop() stopDatabases(); } -PassRefPtr<DatabaseBackendContext> DatabaseContext::backend() +bool DatabaseContext::canSuspendForDocumentSuspension() const { - DatabaseBackendContext* backend = static_cast<DatabaseBackendContext*>(this); - return backend; + if (!hasOpenDatabases() || !m_databaseThread) + return true; + + return !m_databaseThread->hasPendingDatabaseActivity(); } DatabaseThread* DatabaseContext::databaseThread() { if (!m_databaseThread && !m_hasOpenDatabases) { -#if PLATFORM(IOS) - MutexLocker lock(m_databaseThreadMutex); -#endif //PLATFORM(IOS) // It's OK to ask for the m_databaseThread after we've requested // termination because we're still using it to execute the closing // of the database. However, it is NOT OK to create a new thread @@ -177,33 +151,16 @@ DatabaseThread* DatabaseContext::databaseThread() // because in that case we already had a database thread and terminated it and should not create another. m_databaseThread = DatabaseThread::create(); if (!m_databaseThread->start()) - m_databaseThread = 0; -#if PLATFORM(IOS) - if (m_databaseThread) - m_databaseThread->setPaused(m_paused); -#endif //PLATFORM(IOS) + m_databaseThread = nullptr; } return m_databaseThread.get(); } -#if PLATFORM(IOS) -void DatabaseContext::setPaused(bool paused) -{ - MutexLocker lock(m_databaseThreadMutex); - - m_paused = paused; - if (m_databaseThread) - m_databaseThread->setPaused(m_paused); -} -#endif // PLATFORM(IOS) - -bool DatabaseContext::stopDatabases(DatabaseTaskSynchronizer* cleanupSync) +bool DatabaseContext::stopDatabases(DatabaseTaskSynchronizer* synchronizer) { - if (m_isRegistered) { - DatabaseManager::manager().unregisterDatabaseContext(this); - m_isRegistered = false; - } + // FIXME: What guarantees this is never called after the script execution context is null? + ASSERT(scriptExecutionContext()); // Though we initiate termination of the DatabaseThread here in // stopDatabases(), we can't clear the m_databaseThread ref till we get to @@ -214,20 +171,26 @@ bool DatabaseContext::stopDatabases(DatabaseTaskSynchronizer* cleanupSync) // why our ref count is 0 then and we're destructing). Then, the // m_databaseThread RefPtr destructor will deref and delete the // DatabaseThread. - - if (m_databaseThread && !m_hasRequestedTermination) { - m_databaseThread->requestTermination(cleanupSync); + bool result = m_databaseThread && !m_hasRequestedTermination; + if (result) { + m_databaseThread->requestTermination(synchronizer); m_hasRequestedTermination = true; - return true; } - return false; + + auto& context = *scriptExecutionContext(); + if (context.databaseContext()) { + ASSERT(context.databaseContext() == this); + context.setDatabaseContext(nullptr); + } + + return result; } bool DatabaseContext::allowDatabaseAccess() const { - if (m_scriptExecutionContext->isDocument()) { - Document* document = toDocument(m_scriptExecutionContext); - if (!document->page() || (document->page()->settings().privateBrowsingEnabled() && !SchemeRegistry::allowsDatabaseAccessInPrivateBrowsing(document->securityOrigin()->protocol()))) + if (is<Document>(*m_scriptExecutionContext)) { + Document& document = downcast<Document>(*m_scriptExecutionContext); + if (!document.page() || (document.page()->usesEphemeralSession() && !SchemeRegistry::allowsDatabaseAccessInPrivateBrowsing(document.securityOrigin().protocol()))) return false; return true; } @@ -238,18 +201,23 @@ bool DatabaseContext::allowDatabaseAccess() const void DatabaseContext::databaseExceededQuota(const String& name, DatabaseDetails details) { - if (m_scriptExecutionContext->isDocument()) { - Document* document = toDocument(m_scriptExecutionContext); - if (Page* page = document->page()) - page->chrome().client().exceededDatabaseQuota(document->frame(), name, details); + if (is<Document>(*m_scriptExecutionContext)) { + Document& document = downcast<Document>(*m_scriptExecutionContext); + if (Page* page = document.page()) + page->chrome().client().exceededDatabaseQuota(*document.frame(), name, details); return; } ASSERT(m_scriptExecutionContext->isWorkerGlobalScope()); - // FIXME: This needs a real implementation; this is a temporary solution for testing. - const unsigned long long defaultQuota = 5 * 1024 * 1024; - DatabaseManager::manager().setQuota(m_scriptExecutionContext->securityOrigin(), defaultQuota); } -} // namespace WebCore +SecurityOriginData DatabaseContext::securityOrigin() const +{ + return SecurityOriginData::fromSecurityOrigin(*m_scriptExecutionContext->securityOrigin()); +} + +bool DatabaseContext::isContextThread() const +{ + return m_scriptExecutionContext->isContextThread(); +} -#endif // ENABLE(SQL_DATABASE) +} // namespace WebCore diff --git a/Source/WebCore/Modules/webdatabase/DatabaseContext.h b/Source/WebCore/Modules/webdatabase/DatabaseContext.h index 48510d3d0..4c3c46363 100644 --- a/Source/WebCore/Modules/webdatabase/DatabaseContext.h +++ b/Source/WebCore/Modules/webdatabase/DatabaseContext.h @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -25,72 +25,61 @@ * */ -#ifndef DatabaseContext_h -#define DatabaseContext_h - -#if ENABLE(SQL_DATABASE) +#pragma once #include "ActiveDOMObject.h" -#include "DatabaseDetails.h" -#include <wtf/Assertions.h> +#include <wtf/RefPtr.h> #include <wtf/ThreadSafeRefCounted.h> #if PLATFORM(IOS) #include <wtf/Threading.h> -#endif // PLATFORM(IOS) +#endif namespace WebCore { class Database; -class DatabaseBackendContext; +class DatabaseDetails; class DatabaseTaskSynchronizer; class DatabaseThread; -class ScriptExecutionContext; +class SecurityOrigin; +struct SecurityOriginData; -class DatabaseContext : public ThreadSafeRefCounted<DatabaseContext>, ActiveDOMObject { +class DatabaseContext final : public ThreadSafeRefCounted<DatabaseContext>, private ActiveDOMObject { public: virtual ~DatabaseContext(); - // For life-cycle management (inherited from ActiveDOMObject): - virtual void contextDestroyed(); - virtual void stop(); - - PassRefPtr<DatabaseBackendContext> backend(); + DatabaseThread* existingDatabaseThread() const { return m_databaseThread.get(); } DatabaseThread* databaseThread(); -#if PLATFORM(IOS) - void setPaused(bool); -#endif // PLATFORM(IOS) void setHasOpenDatabases() { m_hasOpenDatabases = true; } - bool hasOpenDatabases() { return m_hasOpenDatabases; } + bool hasOpenDatabases() const { return m_hasOpenDatabases; } - // When the database cleanup is done, cleanupSync will be signalled. + // When the database cleanup is done, the sychronizer will be signalled. bool stopDatabases(DatabaseTaskSynchronizer*); bool allowDatabaseAccess() const; void databaseExceededQuota(const String& name, DatabaseDetails); + using ActiveDOMObject::scriptExecutionContext; + SecurityOriginData securityOrigin() const; + + bool isContextThread() const; + private: - explicit DatabaseContext(ScriptExecutionContext*); + explicit DatabaseContext(ScriptExecutionContext&); - void stopDatabases() { stopDatabases(0); } + void stopDatabases() { stopDatabases(nullptr); } + + void contextDestroyed() override; + void stop() override; + bool canSuspendForDocumentSuspension() const override; + const char* activeDOMObjectName() const override { return "DatabaseContext"; } RefPtr<DatabaseThread> m_databaseThread; - bool m_hasOpenDatabases; // This never changes back to false, even after the database thread is closed. - bool m_isRegistered; - bool m_hasRequestedTermination; + bool m_hasOpenDatabases { false }; // This never changes back to false, even after the database thread is closed. + bool m_hasRequestedTermination { false }; - friend class DatabaseBackendContext; friend class DatabaseManager; - -#if PLATFORM(IOS) - Mutex m_databaseThreadMutex; - bool m_paused; -#endif // PLATFORM(IOS) }; } // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) - -#endif // DatabaseContext_h diff --git a/Source/WebCore/Modules/webdatabase/DatabaseDetails.h b/Source/WebCore/Modules/webdatabase/DatabaseDetails.h index 61cd7286d..239803884 100644 --- a/Source/WebCore/Modules/webdatabase/DatabaseDetails.h +++ b/Source/WebCore/Modules/webdatabase/DatabaseDetails.h @@ -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. * @@ -26,10 +26,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DatabaseDetails_h -#define DatabaseDetails_h - -#if ENABLE(SQL_DATABASE) +#pragma once #include <thread> #include <wtf/text/WTFString.h> @@ -85,7 +82,3 @@ private: }; } // namespace WebCore - -#endif - -#endif // DatabaseDetails_h diff --git a/Source/WebCore/Modules/webdatabase/DatabaseError.h b/Source/WebCore/Modules/webdatabase/DatabaseError.h deleted file mode 100644 index 3611c9470..000000000 --- a/Source/WebCore/Modules/webdatabase/DatabaseError.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 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. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * 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. - */ - -#ifndef DatabaseError_h -#define DatabaseError_h - -#if ENABLE(SQL_DATABASE) - -namespace WebCore { - -enum class DatabaseError { - None = 0, - DatabaseIsBeingDeleted, - DatabaseSizeExceededQuota, - DatabaseSizeOverflowed, - GenericSecurityError, - InvalidDatabaseState -}; - -} // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) - -#endif // DatabaseError_h 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) diff --git a/Source/WebCore/Modules/webdatabase/DatabaseManager.h b/Source/WebCore/Modules/webdatabase/DatabaseManager.h index 805b32c94..2ca2d077c 100644 --- a/Source/WebCore/Modules/webdatabase/DatabaseManager.h +++ b/Source/WebCore/Modules/webdatabase/DatabaseManager.h @@ -20,144 +20,75 @@ * 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. */ -#ifndef DatabaseManager_h -#define DatabaseManager_h +#pragma once -#if ENABLE(SQL_DATABASE) - -#include "DatabaseBasicTypes.h" #include "DatabaseDetails.h" -#include "DatabaseError.h" -#include <mutex> +#include "ExceptionOr.h" #include <wtf/Assertions.h> #include <wtf/HashMap.h> #include <wtf/HashSet.h> -#include <wtf/PassRefPtr.h> +#include <wtf/Lock.h> #include <wtf/Threading.h> namespace WebCore { -class AbstractDatabaseServer; class Database; -class DatabaseBackendBase; class DatabaseCallback; class DatabaseContext; class DatabaseManagerClient; -class DatabaseSync; class DatabaseTaskSynchronizer; +class Exception; class SecurityOrigin; class ScriptExecutionContext; +struct SecurityOriginData; class DatabaseManager { - WTF_MAKE_NONCOPYABLE(DatabaseManager); WTF_MAKE_FAST_ALLOCATED; + WTF_MAKE_NONCOPYABLE(DatabaseManager); + friend class WTF::NeverDestroyed<DatabaseManager>; public: - static DatabaseManager& manager(); + WEBCORE_EXPORT static DatabaseManager& singleton(); - void initialize(const String& databasePath); - void setClient(DatabaseManagerClient*); - String databaseDirectoryPath() const; - void setDatabaseDirectoryPath(const String&); + WEBCORE_EXPORT void initialize(const String& databasePath); + WEBCORE_EXPORT void setClient(DatabaseManagerClient*); bool isAvailable(); - void setIsAvailable(bool); + WEBCORE_EXPORT void setIsAvailable(bool); // This gets a DatabaseContext for the specified ScriptExecutionContext. // If one doesn't already exist, it will create a new one. - PassRefPtr<DatabaseContext> databaseContextFor(ScriptExecutionContext*); - - // These 2 methods are for DatabaseContext (un)registration, and should only - // be called by the DatabaseContext constructor and destructor. - void registerDatabaseContext(DatabaseContext*); - void unregisterDatabaseContext(DatabaseContext*); + Ref<DatabaseContext> databaseContext(ScriptExecutionContext&); -#if !ASSERT_DISABLED - void didConstructDatabaseContext(); - void didDestructDatabaseContext(); -#else - void didConstructDatabaseContext() { } - void didDestructDatabaseContext() { } -#endif + ExceptionOr<Ref<Database>> openDatabase(ScriptExecutionContext&, const String& name, const String& expectedVersion, const String& displayName, unsigned estimatedSize, RefPtr<DatabaseCallback>&&); - static ExceptionCode exceptionCodeForDatabaseError(DatabaseError); + WEBCORE_EXPORT bool hasOpenDatabases(ScriptExecutionContext&); + void stopDatabases(ScriptExecutionContext&, DatabaseTaskSynchronizer*); - PassRefPtr<Database> openDatabase(ScriptExecutionContext*, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback>, DatabaseError&); - PassRefPtr<DatabaseSync> openDatabaseSync(ScriptExecutionContext*, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback>, DatabaseError&); + String fullPathForDatabase(SecurityOrigin&, const String& name, bool createIfDoesNotExist = true); - bool hasOpenDatabases(ScriptExecutionContext*); - void stopDatabases(ScriptExecutionContext*, DatabaseTaskSynchronizer*); + WEBCORE_EXPORT DatabaseDetails detailsForNameAndOrigin(const String&, SecurityOrigin&); - String fullPathForDatabase(SecurityOrigin*, const String& name, bool createIfDoesNotExist = true); - - bool hasEntryForOrigin(SecurityOrigin*); - void origins(Vector<RefPtr<SecurityOrigin>>& result); - bool databaseNamesForOrigin(SecurityOrigin*, Vector<String>& result); - DatabaseDetails detailsForNameAndOrigin(const String&, SecurityOrigin*); +private: + DatabaseManager() = default; + ~DatabaseManager() = delete; - unsigned long long usageForOrigin(SecurityOrigin*); - unsigned long long quotaForOrigin(SecurityOrigin*); + enum OpenAttempt { FirstTryToOpenDatabase, RetryOpenDatabase }; + ExceptionOr<Ref<Database>> openDatabaseBackend(ScriptExecutionContext&, const String& name, const String& expectedVersion, const String& displayName, unsigned estimatedSize, bool setVersionInNewDatabase); + ExceptionOr<Ref<Database>> tryToOpenDatabaseBackend(ScriptExecutionContext&, const String& name, const String& expectedVersion, const String& displayName, unsigned estimatedSize, bool setVersionInNewDatabase, OpenAttempt); - void setQuota(SecurityOrigin*, unsigned long long); + class ProposedDatabase; + void addProposedDatabase(ProposedDatabase&); + void removeProposedDatabase(ProposedDatabase&); - void deleteAllDatabases(); - bool deleteOrigin(SecurityOrigin*); - bool deleteDatabase(SecurityOrigin*, const String& name); + static void logErrorMessage(ScriptExecutionContext&, const String& message); - void interruptAllDatabasesForContext(ScriptExecutionContext*); + DatabaseManagerClient* m_client { nullptr }; + bool m_databaseIsAvailable { true }; -private: - class ProposedDatabase { - public: - ProposedDatabase(DatabaseManager&, SecurityOrigin*, - const String& name, const String& displayName, unsigned long estimatedSize); - ~ProposedDatabase(); - - SecurityOrigin* origin() { return m_origin.get(); } - DatabaseDetails& details() { return m_details; } - - private: - DatabaseManager& m_manager; - RefPtr<SecurityOrigin> m_origin; - DatabaseDetails m_details; - }; - - DatabaseManager(); - ~DatabaseManager() { } - - // This gets a DatabaseContext for the specified ScriptExecutionContext if - // it already exist previously. Otherwise, it returns 0. - PassRefPtr<DatabaseContext> existingDatabaseContextFor(ScriptExecutionContext*); - - PassRefPtr<DatabaseBackendBase> openDatabaseBackend(ScriptExecutionContext*, - DatabaseType, const String& name, const String& expectedVersion, const String& displayName, - unsigned long estimatedSize, bool setVersionInNewDatabase, DatabaseError&, String& errorMessage); - - void addProposedDatabase(ProposedDatabase*); - void removeProposedDatabase(ProposedDatabase*); - - static void logErrorMessage(ScriptExecutionContext*, const String& message); - - AbstractDatabaseServer* m_server; - DatabaseManagerClient* m_client; - bool m_databaseIsAvailable; - - // Access to the following fields require locking m_lock below: - typedef HashMap<ScriptExecutionContext*, DatabaseContext*> ContextMap; - ContextMap m_contextMap; -#if !ASSERT_DISABLED - int m_databaseContextRegisteredCount; - int m_databaseContextInstanceCount; -#endif + Lock m_proposedDatabasesMutex; HashSet<ProposedDatabase*> m_proposedDatabases; - - // This mutex protects m_contextMap, and m_proposedDatabases. - std::mutex m_mutex; }; } // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) - -#endif // DatabaseManager_h diff --git a/Source/WebCore/Modules/webdatabase/DatabaseManagerClient.h b/Source/WebCore/Modules/webdatabase/DatabaseManagerClient.h index afc69f8cb..afd568ff4 100644 --- a/Source/WebCore/Modules/webdatabase/DatabaseManagerClient.h +++ b/Source/WebCore/Modules/webdatabase/DatabaseManagerClient.h @@ -22,32 +22,30 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DatabaseManagerClient_h -#define DatabaseManagerClient_h -#if ENABLE(SQL_DATABASE) +#pragma once #include <wtf/Forward.h> namespace WebCore { -class SecurityOrigin; +struct SecurityOriginData; class DatabaseManagerClient { public: virtual ~DatabaseManagerClient() { } - virtual void dispatchDidModifyOrigin(SecurityOrigin*) = 0; - virtual void dispatchDidModifyDatabase(SecurityOrigin*, const String& databaseName) = 0; + virtual void dispatchDidModifyOrigin(const SecurityOriginData&) = 0; + virtual void dispatchDidModifyDatabase(const SecurityOriginData&, const String& databaseName) = 0; #if PLATFORM(IOS) - virtual void dispatchDidAddNewOrigin(SecurityOrigin*) = 0; + virtual void dispatchDidAddNewOrigin() = 0; virtual void dispatchDidDeleteDatabase() = 0; virtual void dispatchDidDeleteDatabaseOrigin() = 0; +#else + static void dispatchDidAddNewOrigin() { } + static void dispatchDidDeleteDatabase() { } + static void dispatchDidDeleteDatabaseOrigin() { } #endif }; } // namespace WebCore - -#endif - -#endif // DatabaseManagerClient_h diff --git a/Source/WebCore/Modules/webdatabase/DatabaseServer.cpp b/Source/WebCore/Modules/webdatabase/DatabaseServer.cpp deleted file mode 100644 index 7f2cb3f8e..000000000 --- a/Source/WebCore/Modules/webdatabase/DatabaseServer.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 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. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * 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. - */ - -#include "config.h" -#include "DatabaseServer.h" - -#if ENABLE(SQL_DATABASE) - -#include "Database.h" -#include "DatabaseBackend.h" -#include "DatabaseBackendContext.h" -#include "DatabaseBackendSync.h" -#include "DatabaseSync.h" -#include "DatabaseTracker.h" - -namespace WebCore { - -void DatabaseServer::initialize(const String& databasePath) -{ - DatabaseTracker::initializeTracker(databasePath); -} - -void DatabaseServer::setClient(DatabaseManagerClient* client) -{ - DatabaseTracker::tracker().setClient(client); -} - -String DatabaseServer::databaseDirectoryPath() const -{ - return DatabaseTracker::tracker().databaseDirectoryPath(); -} - -void DatabaseServer::setDatabaseDirectoryPath(const String& path) -{ - DatabaseTracker::tracker().setDatabaseDirectoryPath(path); -} - -String DatabaseServer::fullPathForDatabase(SecurityOrigin* origin, const String& name, bool createIfDoesNotExist) -{ - return DatabaseTracker::tracker().fullPathForDatabase(origin, name, createIfDoesNotExist); -} - -bool DatabaseServer::hasEntryForOrigin(SecurityOrigin* origin) -{ - return DatabaseTracker::tracker().hasEntryForOrigin(origin); -} - -void DatabaseServer::origins(Vector<RefPtr<SecurityOrigin>>& result) -{ - DatabaseTracker::tracker().origins(result); -} - -bool DatabaseServer::databaseNamesForOrigin(SecurityOrigin* origin, Vector<String>& result) -{ - return DatabaseTracker::tracker().databaseNamesForOrigin(origin, result); -} - -DatabaseDetails DatabaseServer::detailsForNameAndOrigin(const String& name, SecurityOrigin* origin) -{ - return DatabaseTracker::tracker().detailsForNameAndOrigin(name, origin); -} - -unsigned long long DatabaseServer::usageForOrigin(SecurityOrigin* origin) -{ - return DatabaseTracker::tracker().usageForOrigin(origin); -} - -unsigned long long DatabaseServer::quotaForOrigin(SecurityOrigin* origin) -{ - return DatabaseTracker::tracker().quotaForOrigin(origin); -} - -void DatabaseServer::setQuota(SecurityOrigin* origin, unsigned long long quotaSize) -{ - DatabaseTracker::tracker().setQuota(origin, quotaSize); -} - -void DatabaseServer::deleteAllDatabases() -{ - DatabaseTracker::tracker().deleteAllDatabases(); -} - -bool DatabaseServer::deleteOrigin(SecurityOrigin* origin) -{ - return DatabaseTracker::tracker().deleteOrigin(origin); -} - -bool DatabaseServer::deleteDatabase(SecurityOrigin* origin, const String& name) -{ - return DatabaseTracker::tracker().deleteDatabase(origin, name); -} - -void DatabaseServer::interruptAllDatabasesForContext(const DatabaseBackendContext* context) -{ - DatabaseTracker::tracker().interruptAllDatabasesForContext(context); -} - -PassRefPtr<DatabaseBackendBase> DatabaseServer::openDatabase(RefPtr<DatabaseBackendContext>& backendContext, - DatabaseType type, const String& name, const String& expectedVersion, const String& displayName, - unsigned long estimatedSize, bool setVersionInNewDatabase, DatabaseError &error, String& errorMessage, - OpenAttempt attempt) -{ - RefPtr<DatabaseBackendBase> database; - bool success = false; // Make some older compilers happy. - - switch (attempt) { - case FirstTryToOpenDatabase: - success = DatabaseTracker::tracker().canEstablishDatabase(backendContext.get(), name, estimatedSize, error); - break; - case RetryOpenDatabase: - success = DatabaseTracker::tracker().retryCanEstablishDatabase(backendContext.get(), name, estimatedSize, error); - } - - if (success) - database = createDatabase(backendContext, type, name, expectedVersion, displayName, estimatedSize, setVersionInNewDatabase, error, errorMessage); - return database.release(); -} - -PassRefPtr<DatabaseBackendBase> DatabaseServer::createDatabase(RefPtr<DatabaseBackendContext>& backendContext, - DatabaseType type, const String& name, const String& expectedVersion, const String& displayName, - unsigned long estimatedSize, bool setVersionInNewDatabase, DatabaseError& error, String& errorMessage) -{ - RefPtr<DatabaseBackendBase> database; - switch (type) { - case DatabaseType::Async: - database = adoptRef(new Database(backendContext, name, expectedVersion, displayName, estimatedSize)); - break; - case DatabaseType::Sync: - database = adoptRef(new DatabaseSync(backendContext, name, expectedVersion, displayName, estimatedSize)); - } - - if (!database->openAndVerifyVersion(setVersionInNewDatabase, error, errorMessage)) - return 0; - - DatabaseTracker::tracker().setDatabaseDetails(backendContext->securityOrigin(), name, displayName, estimatedSize); - return database.release(); -} - -} // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) diff --git a/Source/WebCore/Modules/webdatabase/DatabaseServer.h b/Source/WebCore/Modules/webdatabase/DatabaseServer.h deleted file mode 100644 index 7cf9c9c9f..000000000 --- a/Source/WebCore/Modules/webdatabase/DatabaseServer.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 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. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * 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. - */ - -#ifndef DatabaseServer_h -#define DatabaseServer_h - -#if ENABLE(SQL_DATABASE) - -#include "AbstractDatabaseServer.h" - -namespace WebCore { - -class DatabaseServer: public AbstractDatabaseServer { -public: - DatabaseServer() { }; - virtual ~DatabaseServer() { } - - virtual void initialize(const String& databasePath); - - virtual void setClient(DatabaseManagerClient*); - virtual String databaseDirectoryPath() const; - virtual void setDatabaseDirectoryPath(const String&); - - virtual String fullPathForDatabase(SecurityOrigin*, const String& name, bool createIfDoesNotExist); - - virtual PassRefPtr<DatabaseBackendBase> openDatabase(RefPtr<DatabaseBackendContext>&, DatabaseType, - const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize, - bool setVersionInNewDatabase, DatabaseError&, String& errorMessage, OpenAttempt); - - virtual bool hasEntryForOrigin(SecurityOrigin*); - virtual void origins(Vector<RefPtr<SecurityOrigin>>& result); - virtual bool databaseNamesForOrigin(SecurityOrigin*, Vector<String>& result); - virtual DatabaseDetails detailsForNameAndOrigin(const String&, SecurityOrigin*); - - virtual unsigned long long usageForOrigin(SecurityOrigin*); - virtual unsigned long long quotaForOrigin(SecurityOrigin*); - - virtual void setQuota(SecurityOrigin*, unsigned long long); - - virtual void deleteAllDatabases(); - virtual bool deleteOrigin(SecurityOrigin*); - virtual bool deleteDatabase(SecurityOrigin*, const String& name); - - virtual void interruptAllDatabasesForContext(const DatabaseBackendContext*); - -protected: - virtual PassRefPtr<DatabaseBackendBase> createDatabase(RefPtr<DatabaseBackendContext>&, DatabaseType, - const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize, - bool setVersionInNewDatabase, DatabaseError&, String& errorMessage); -}; - -} // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) - -#endif // DatabaseServer_h diff --git a/Source/WebCore/Modules/webdatabase/DatabaseSync.cpp b/Source/WebCore/Modules/webdatabase/DatabaseSync.cpp deleted file mode 100644 index f92c34ff0..000000000 --- a/Source/WebCore/Modules/webdatabase/DatabaseSync.cpp +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * 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. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR 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. - */ - -#include "config.h" -#include "DatabaseSync.h" - -#if ENABLE(SQL_DATABASE) - -#include "DatabaseBackendContext.h" -#include "DatabaseBackendSync.h" -#include "DatabaseCallback.h" -#include "DatabaseContext.h" -#include "DatabaseManager.h" -#include "DatabaseTracker.h" -#include "Logging.h" -#include "SQLException.h" -#include "SQLTransactionSync.h" -#include "SQLTransactionSyncCallback.h" -#include "ScriptExecutionContext.h" -#include "SecurityOrigin.h" -#include <wtf/PassRefPtr.h> -#include <wtf/RefPtr.h> -#include <wtf/text/CString.h> - -namespace WebCore { - -PassRefPtr<DatabaseSync> DatabaseSync::create(ScriptExecutionContext*, PassRefPtr<DatabaseBackendBase> backend) -{ - return static_cast<DatabaseSync*>(backend.get()); -} - -DatabaseSync::DatabaseSync(PassRefPtr<DatabaseBackendContext> databaseContext, - const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize) - : DatabaseBase(databaseContext->scriptExecutionContext()) - , DatabaseBackendSync(databaseContext, name, expectedVersion, displayName, estimatedSize) -{ - setFrontend(this); -} - -DatabaseSync::~DatabaseSync() -{ - ASSERT(m_scriptExecutionContext->isContextThread()); -} - -PassRefPtr<DatabaseBackendSync> DatabaseSync::backend() -{ - return this; -} - -void DatabaseSync::changeVersion(const String& oldVersion, const String& newVersion, PassRefPtr<SQLTransactionSyncCallback> changeVersionCallback, ExceptionCode& ec) -{ - ASSERT(m_scriptExecutionContext->isContextThread()); - - if (sqliteDatabase().transactionInProgress()) { - setLastErrorMessage("unable to changeVersion from within a transaction"); - ec = SQLException::DATABASE_ERR; - return; - } - - RefPtr<SQLTransactionSync> transaction = SQLTransactionSync::create(this, changeVersionCallback, false); - if ((ec = transaction->begin())) { - ASSERT(!lastErrorMessage().isEmpty()); - return; - } - - String actualVersion; - if (!getVersionFromDatabase(actualVersion)) { - setLastErrorMessage("unable to read the current version", sqliteDatabase().lastError(), sqliteDatabase().lastErrorMsg()); - ec = SQLException::UNKNOWN_ERR; - return; - } - - if (actualVersion != oldVersion) { - setLastErrorMessage("current version of the database and `oldVersion` argument do not match"); - ec = SQLException::VERSION_ERR; - return; - } - - if ((ec = transaction->execute())) { - ASSERT(!lastErrorMessage().isEmpty()); - return; - } - - if (!setVersionInDatabase(newVersion)) { - setLastErrorMessage("unable to set the new version", sqliteDatabase().lastError(), sqliteDatabase().lastErrorMsg()); - ec = SQLException::UNKNOWN_ERR; - return; - } - - if ((ec = transaction->commit())) { - ASSERT(!lastErrorMessage().isEmpty()); - setCachedVersion(oldVersion); - return; - } - - setExpectedVersion(newVersion); - setLastErrorMessage(""); -} - -void DatabaseSync::transaction(PassRefPtr<SQLTransactionSyncCallback> callback, ExceptionCode& ec) -{ - runTransaction(callback, false, ec); -} - -void DatabaseSync::readTransaction(PassRefPtr<SQLTransactionSyncCallback> callback, ExceptionCode& ec) -{ - runTransaction(callback, true, ec); -} - -void DatabaseSync::runTransaction(PassRefPtr<SQLTransactionSyncCallback> callback, bool readOnly, ExceptionCode& ec) -{ - ASSERT(m_scriptExecutionContext->isContextThread()); - - if (sqliteDatabase().transactionInProgress()) { - setLastErrorMessage("unable to start a transaction from within a transaction"); - ec = SQLException::DATABASE_ERR; - return; - } - - RefPtr<SQLTransactionSync> transaction = SQLTransactionSync::create(this, callback, readOnly); - if ((ec = transaction->begin()) || (ec = transaction->execute()) || (ec = transaction->commit())) { - ASSERT(!lastErrorMessage().isEmpty()); - transaction->rollback(); - } - - setLastErrorMessage(""); -} - -void DatabaseSync::markAsDeletedAndClose() -{ - // FIXME: need to do something similar to closeImmediately(), but in a sync way -} - -class CloseSyncDatabaseOnContextThreadTask : public ScriptExecutionContext::Task { -public: - static PassOwnPtr<CloseSyncDatabaseOnContextThreadTask> create(PassRefPtr<DatabaseSync> database) - { - return adoptPtr(new CloseSyncDatabaseOnContextThreadTask(database)); - } - - virtual void performTask(ScriptExecutionContext*) - { - m_database->closeImmediately(); - } - -private: - CloseSyncDatabaseOnContextThreadTask(PassRefPtr<DatabaseSync> database) - : m_database(database) - { - } - - RefPtr<DatabaseSync> m_database; -}; - -void DatabaseSync::closeImmediately() -{ - ASSERT(m_scriptExecutionContext->isContextThread()); - - if (!opened()) - return; - - logErrorMessage("forcibly closing database"); - closeDatabase(); -} - -} // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) diff --git a/Source/WebCore/Modules/webdatabase/DatabaseSync.h b/Source/WebCore/Modules/webdatabase/DatabaseSync.h deleted file mode 100644 index 4119b5bbe..000000000 --- a/Source/WebCore/Modules/webdatabase/DatabaseSync.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * 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. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR 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. - */ - -#ifndef DatabaseSync_h -#define DatabaseSync_h - -#if ENABLE(SQL_DATABASE) - -#include "DatabaseBackendSync.h" -#include "DatabaseBase.h" -#include "DatabaseBasicTypes.h" -#include <wtf/Forward.h> -#include <wtf/text/WTFString.h> - -namespace WebCore { - -class DatabaseCallback; -class SQLTransactionSync; -class SQLTransactionSyncCallback; - -// Instances of this class should be created and used only on the worker's context thread. -class DatabaseSync : public DatabaseBase, public DatabaseBackendSync { -public: - virtual ~DatabaseSync(); - - void changeVersion(const String& oldVersion, const String& newVersion, PassRefPtr<SQLTransactionSyncCallback>, ExceptionCode&); - void transaction(PassRefPtr<SQLTransactionSyncCallback>, ExceptionCode&); - void readTransaction(PassRefPtr<SQLTransactionSyncCallback>, ExceptionCode&); - - virtual void markAsDeletedAndClose(); - virtual void closeImmediately(); - - const String& lastErrorMessage() const { return m_lastErrorMessage; } - void setLastErrorMessage(const String& message) { m_lastErrorMessage = message; } - void setLastErrorMessage(const char* message, int sqliteCode) - { - setLastErrorMessage(String::format("%s (%d)", message, sqliteCode)); - } - void setLastErrorMessage(const char* message, int sqliteCode, const char* sqliteMessage) - { - setLastErrorMessage(String::format("%s (%d, %s)", message, sqliteCode, sqliteMessage)); - } - -private: - DatabaseSync(PassRefPtr<DatabaseBackendContext>, const String& name, - const String& expectedVersion, const String& displayName, unsigned long estimatedSize); - PassRefPtr<DatabaseBackendSync> backend(); - static PassRefPtr<DatabaseSync> create(ScriptExecutionContext*, PassRefPtr<DatabaseBackendBase>); - - void runTransaction(PassRefPtr<SQLTransactionSyncCallback>, bool readOnly, ExceptionCode&); - - String m_lastErrorMessage; - - friend class DatabaseManager; - friend class DatabaseServer; // FIXME: remove this when the backend has been split out. -}; - -} // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) - -#endif diff --git a/Source/WebCore/Modules/webdatabase/DatabaseSync.idl b/Source/WebCore/Modules/webdatabase/DatabaseSync.idl deleted file mode 100644 index 4b5ff2a6f..000000000 --- a/Source/WebCore/Modules/webdatabase/DatabaseSync.idl +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * 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. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR 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. - */ - -[ - NoInterfaceObject, - Conditional=SQL_DATABASE, - JSNoStaticTables, -] interface DatabaseSync { - readonly attribute DOMString version; - readonly attribute DOMString lastErrorMessage; - [RaisesException] void changeVersion(DOMString oldVersion, DOMString newVersion, optional SQLTransactionSyncCallback callback); - [RaisesException] void transaction(SQLTransactionSyncCallback callback); - [RaisesException] void readTransaction(SQLTransactionSyncCallback callback); -}; - diff --git a/Source/WebCore/Modules/webdatabase/DatabaseTask.cpp b/Source/WebCore/Modules/webdatabase/DatabaseTask.cpp index 8cf8ea21a..f1a54ac94 100644 --- a/Source/WebCore/Modules/webdatabase/DatabaseTask.cpp +++ b/Source/WebCore/Modules/webdatabase/DatabaseTask.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2013, 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -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. * @@ -25,22 +25,17 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + #include "config.h" #include "DatabaseTask.h" -#if ENABLE(SQL_DATABASE) - #include "Database.h" -#include "DatabaseBackend.h" #include "Logging.h" +#include "SQLTransaction.h" namespace WebCore { DatabaseTaskSynchronizer::DatabaseTaskSynchronizer() - : m_taskCompleted(false) -#ifndef NDEBUG - , m_hasCheckedForTermination(false) -#endif { } @@ -56,48 +51,36 @@ void DatabaseTaskSynchronizer::taskCompleted() { m_synchronousMutex.lock(); m_taskCompleted = true; - m_synchronousCondition.signal(); + m_synchronousCondition.notifyOne(); m_synchronousMutex.unlock(); } -DatabaseTask::DatabaseTask(DatabaseBackend* database, DatabaseTaskSynchronizer* synchronizer) +DatabaseTask::DatabaseTask(Database& database, DatabaseTaskSynchronizer* synchronizer) : m_database(database) , m_synchronizer(synchronizer) -#if !LOG_DISABLED - , m_complete(false) -#endif { } DatabaseTask::~DatabaseTask() { -#if !LOG_DISABLED ASSERT(m_complete || !m_synchronizer); -#endif } void DatabaseTask::performTask() { // Database tasks are meant to be used only once, so make sure this one hasn't been performed before. -#if !LOG_DISABLED ASSERT(!m_complete); -#endif LOG(StorageAPI, "Performing %s %p\n", debugTaskName(), this); -#if !PLATFORM(IOS) - m_database->resetAuthorizer(); -#else - if (m_database) - m_database->resetAuthorizer(); -#endif + m_database.resetAuthorizer(); doPerformTask(); if (m_synchronizer) m_synchronizer->taskCompleted(); -#if !LOG_DISABLED +#if !ASSERT_DISABLED m_complete = true; #endif } @@ -105,62 +88,60 @@ void DatabaseTask::performTask() // *** DatabaseOpenTask *** // Opens the database file and verifies the version matches the expected version. -DatabaseBackend::DatabaseOpenTask::DatabaseOpenTask(DatabaseBackend* database, bool setVersionInNewDatabase, DatabaseTaskSynchronizer* synchronizer, DatabaseError& error, String& errorMessage, bool& success) - : DatabaseTask(database, synchronizer) +DatabaseOpenTask::DatabaseOpenTask(Database& database, bool setVersionInNewDatabase, DatabaseTaskSynchronizer& synchronizer, ExceptionOr<void>& result) + : DatabaseTask(database, &synchronizer) , m_setVersionInNewDatabase(setVersionInNewDatabase) - , m_error(error) - , m_errorMessage(errorMessage) - , m_success(success) + , m_result(result) { - ASSERT(synchronizer); // A task with output parameters is supposed to be synchronous. } -void DatabaseBackend::DatabaseOpenTask::doPerformTask() +void DatabaseOpenTask::doPerformTask() { - String errorMessage; - m_success = database()->performOpenAndVerify(m_setVersionInNewDatabase, m_error, errorMessage); - if (!m_success) - m_errorMessage = errorMessage.isolatedCopy(); + m_result = isolatedCopy(database().performOpenAndVerify(m_setVersionInNewDatabase)); } #if !LOG_DISABLED -const char* DatabaseBackend::DatabaseOpenTask::debugTaskName() const + +const char* DatabaseOpenTask::debugTaskName() const { return "DatabaseOpenTask"; } + #endif // *** DatabaseCloseTask *** // Closes the database. -DatabaseBackend::DatabaseCloseTask::DatabaseCloseTask(DatabaseBackend* database, DatabaseTaskSynchronizer* synchronizer) - : DatabaseTask(database, synchronizer) +DatabaseCloseTask::DatabaseCloseTask(Database& database, DatabaseTaskSynchronizer& synchronizer) + : DatabaseTask(database, &synchronizer) { } -void DatabaseBackend::DatabaseCloseTask::doPerformTask() +void DatabaseCloseTask::doPerformTask() { - Database::from(database())->close(); + database().performClose(); } #if !LOG_DISABLED -const char* DatabaseBackend::DatabaseCloseTask::debugTaskName() const + +const char* DatabaseCloseTask::debugTaskName() const { return "DatabaseCloseTask"; } + #endif // *** DatabaseTransactionTask *** // Starts a transaction that will report its results via a callback. -DatabaseBackend::DatabaseTransactionTask::DatabaseTransactionTask(PassRefPtr<SQLTransactionBackend> transaction) - : DatabaseTask(Database::from(transaction->database()), 0) - , m_transaction(transaction) +DatabaseTransactionTask::DatabaseTransactionTask(RefPtr<SQLTransaction>&& transaction) + : DatabaseTask(transaction->database(), 0) + , m_transaction(WTFMove(transaction)) , m_didPerformTask(false) { } -DatabaseBackend::DatabaseTransactionTask::~DatabaseTransactionTask() +DatabaseTransactionTask::~DatabaseTransactionTask() { // If the task is being destructed without the transaction ever being run, // then we must either have an error or an interruption. Give the @@ -174,48 +155,43 @@ DatabaseBackend::DatabaseTransactionTask::~DatabaseTransactionTask() m_transaction->notifyDatabaseThreadIsShuttingDown(); } -#if PLATFORM(IOS) -bool Database::DatabaseTransactionTask::shouldPerformWhilePaused() const -{ - return m_transaction->shouldPerformWhilePaused(); -} -#endif - -void DatabaseBackend::DatabaseTransactionTask::doPerformTask() +void DatabaseTransactionTask::doPerformTask() { m_transaction->performNextStep(); m_didPerformTask = true; } #if !LOG_DISABLED -const char* DatabaseBackend::DatabaseTransactionTask::debugTaskName() const + +const char* DatabaseTransactionTask::debugTaskName() const { return "DatabaseTransactionTask"; } + #endif // *** DatabaseTableNamesTask *** // Retrieves a list of all tables in the database - for WebInspector support. -DatabaseBackend::DatabaseTableNamesTask::DatabaseTableNamesTask(DatabaseBackend* database, DatabaseTaskSynchronizer* synchronizer, Vector<String>& names) - : DatabaseTask(database, synchronizer) - , m_tableNames(names) +DatabaseTableNamesTask::DatabaseTableNamesTask(Database& database, DatabaseTaskSynchronizer& synchronizer, Vector<String>& result) + : DatabaseTask(database, &synchronizer) + , m_result(result) { - ASSERT(synchronizer); // A task with output parameters is supposed to be synchronous. } -void DatabaseBackend::DatabaseTableNamesTask::doPerformTask() +void DatabaseTableNamesTask::doPerformTask() { - m_tableNames = Database::from(database())->performGetTableNames(); + // FIXME: Why no need for an isolatedCopy here? + m_result = database().performGetTableNames(); } #if !LOG_DISABLED -const char* DatabaseBackend::DatabaseTableNamesTask::debugTaskName() const + +const char* DatabaseTableNamesTask::debugTaskName() const { return "DatabaseTableNamesTask"; } + #endif } // namespace WebCore - -#endif diff --git a/Source/WebCore/Modules/webdatabase/DatabaseTask.h b/Source/WebCore/Modules/webdatabase/DatabaseTask.h index 1edaf8661..fe49bcdc1 100644 --- a/Source/WebCore/Modules/webdatabase/DatabaseTask.h +++ b/Source/WebCore/Modules/webdatabase/DatabaseTask.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2013, 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -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. * @@ -25,21 +25,14 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DatabaseTask_h -#define DatabaseTask_h -#if ENABLE(SQL_DATABASE) +#pragma once -#include "DatabaseBackend.h" -#include "DatabaseBasicTypes.h" -#include "DatabaseError.h" +#include "ExceptionOr.h" #include "SQLTransactionBackend.h" -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> -#include <wtf/PassRefPtr.h> -#include <wtf/Threading.h> +#include <wtf/Condition.h> +#include <wtf/Lock.h> #include <wtf/Vector.h> -#include <wtf/text/WTFString.h> namespace WebCore { @@ -62,143 +55,103 @@ public: #endif private: - bool m_taskCompleted; - Mutex m_synchronousMutex; - ThreadCondition m_synchronousCondition; + bool m_taskCompleted { false }; + Lock m_synchronousMutex; + Condition m_synchronousCondition; #ifndef NDEBUG - bool m_hasCheckedForTermination; + bool m_hasCheckedForTermination { false }; #endif }; class DatabaseTask { - WTF_MAKE_NONCOPYABLE(DatabaseTask); WTF_MAKE_FAST_ALLOCATED; + WTF_MAKE_FAST_ALLOCATED; public: virtual ~DatabaseTask(); -#if PLATFORM(IOS) - virtual bool shouldPerformWhilePaused() const = 0; -#endif - void performTask(); - DatabaseBackend* database() const { return m_database; } -#ifndef NDEBUG + Database& database() const { return m_database; } + +#if !ASSERT_DISABLED bool hasSynchronizer() const { return m_synchronizer; } bool hasCheckedForTermination() const { return m_synchronizer->hasCheckedForTermination(); } #endif protected: - DatabaseTask(DatabaseBackend*, DatabaseTaskSynchronizer*); + DatabaseTask(Database&, DatabaseTaskSynchronizer*); private: virtual void doPerformTask() = 0; - DatabaseBackend* m_database; + Database& m_database; DatabaseTaskSynchronizer* m_synchronizer; #if !LOG_DISABLED virtual const char* debugTaskName() const = 0; - bool m_complete; +#endif + +#if !ASSERT_DISABLED + bool m_complete { false }; #endif }; -class DatabaseBackend::DatabaseOpenTask : public DatabaseTask { +class DatabaseOpenTask final : public DatabaseTask { public: - static std::unique_ptr<DatabaseOpenTask> create(DatabaseBackend* db, bool setVersionInNewDatabase, DatabaseTaskSynchronizer* synchronizer, DatabaseError& error, String& errorMessage, bool& success) - { - return std::unique_ptr<DatabaseOpenTask>(new DatabaseOpenTask(db, setVersionInNewDatabase, synchronizer, error, errorMessage, success)); - } - -#if PLATFORM(IOS) - virtual bool shouldPerformWhilePaused() const override { return true; } -#endif + DatabaseOpenTask(Database&, bool setVersionInNewDatabase, DatabaseTaskSynchronizer&, ExceptionOr<void>& result); private: - DatabaseOpenTask(DatabaseBackend*, bool setVersionInNewDatabase, DatabaseTaskSynchronizer*, DatabaseError&, String& errorMessage, bool& success); + void doPerformTask() final; - virtual void doPerformTask(); #if !LOG_DISABLED - virtual const char* debugTaskName() const; + const char* debugTaskName() const final; #endif bool m_setVersionInNewDatabase; - DatabaseError& m_error; - String& m_errorMessage; - bool& m_success; + ExceptionOr<void>& m_result; }; -class DatabaseBackend::DatabaseCloseTask : public DatabaseTask { +class DatabaseCloseTask final : public DatabaseTask { public: - static std::unique_ptr<DatabaseCloseTask> create(DatabaseBackend* db, DatabaseTaskSynchronizer* synchronizer) - { - return std::unique_ptr<DatabaseCloseTask>(new DatabaseCloseTask(db, synchronizer)); - } - -#if PLATFORM(IOS) - virtual bool shouldPerformWhilePaused() const override { return true; } -#endif + DatabaseCloseTask(Database&, DatabaseTaskSynchronizer&); private: - DatabaseCloseTask(DatabaseBackend*, DatabaseTaskSynchronizer*); + void doPerformTask() final; - virtual void doPerformTask(); #if !LOG_DISABLED - virtual const char* debugTaskName() const; + const char* debugTaskName() const final; #endif }; -class DatabaseBackend::DatabaseTransactionTask : public DatabaseTask { +class DatabaseTransactionTask final : public DatabaseTask { public: + explicit DatabaseTransactionTask(RefPtr<SQLTransaction>&&); virtual ~DatabaseTransactionTask(); - // Transaction task is never synchronous, so no 'synchronizer' parameter. - static std::unique_ptr<DatabaseTransactionTask> create(PassRefPtr<SQLTransactionBackend> transaction) - { - return std::unique_ptr<DatabaseTransactionTask>(new DatabaseTransactionTask(transaction)); - } - -#if PLATFORM(IOS) - virtual bool shouldPerformWhilePaused() const override; -#endif - - SQLTransactionBackend* transaction() const { return m_transaction.get(); } + SQLTransaction* transaction() const { return m_transaction.get(); } private: - explicit DatabaseTransactionTask(PassRefPtr<SQLTransactionBackend>); + void doPerformTask() final; - virtual void doPerformTask(); #if !LOG_DISABLED - virtual const char* debugTaskName() const; + const char* debugTaskName() const final; #endif - RefPtr<SQLTransactionBackend> m_transaction; + RefPtr<SQLTransaction> m_transaction; bool m_didPerformTask; }; -class DatabaseBackend::DatabaseTableNamesTask : public DatabaseTask { +class DatabaseTableNamesTask final : public DatabaseTask { public: - static std::unique_ptr<DatabaseTableNamesTask> create(DatabaseBackend* db, DatabaseTaskSynchronizer* synchronizer, Vector<String>& names) - { - return std::unique_ptr<DatabaseTableNamesTask>(new DatabaseTableNamesTask(db, synchronizer, names)); - } - -#if PLATFORM(IOS) - virtual bool shouldPerformWhilePaused() const override { return true; } -#endif + DatabaseTableNamesTask(Database&, DatabaseTaskSynchronizer&, Vector<String>& result); private: - DatabaseTableNamesTask(DatabaseBackend*, DatabaseTaskSynchronizer*, Vector<String>& names); + void doPerformTask() final; - virtual void doPerformTask(); #if !LOG_DISABLED - virtual const char* debugTaskName() const; + const char* debugTaskName() const override; #endif - Vector<String>& m_tableNames; + Vector<String>& m_result; }; } // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) - -#endif // DatabaseTask_h diff --git a/Source/WebCore/Modules/webdatabase/DatabaseThread.cpp b/Source/WebCore/Modules/webdatabase/DatabaseThread.cpp index 7ee764ca0..0dde5f726 100644 --- a/Source/WebCore/Modules/webdatabase/DatabaseThread.cpp +++ b/Source/WebCore/Modules/webdatabase/DatabaseThread.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,25 +29,17 @@ #include "config.h" #include "DatabaseThread.h" -#if ENABLE(SQL_DATABASE) - #include "Database.h" #include "DatabaseTask.h" #include "Logging.h" -#include "SQLTransactionClient.h" +#include "SQLTransaction.h" #include "SQLTransactionCoordinator.h" #include <wtf/AutodrainedPool.h> namespace WebCore { DatabaseThread::DatabaseThread() - : m_threadID(0) -#if PLATFORM(IOS) - , m_paused(false) -#endif - , m_transactionClient(adoptPtr(new SQLTransactionClient())) - , m_transactionCoordinator(adoptPtr(new SQLTransactionCoordinator())) - , m_cleanupSync(0) + : m_transactionCoordinator(std::make_unique<SQLTransactionCoordinator>()) { m_selfRef = this; } @@ -66,7 +58,7 @@ DatabaseThread::~DatabaseThread() bool DatabaseThread::start() { - MutexLocker lock(m_threadCreationMutex); + LockHolder lock(m_threadCreationMutex); if (m_threadID) return true; @@ -76,13 +68,10 @@ bool DatabaseThread::start() return m_threadID; } -void DatabaseThread::requestTermination(DatabaseTaskSynchronizer *cleanupSync) +void DatabaseThread::requestTermination(DatabaseTaskSynchronizer* cleanupSync) { m_cleanupSync = cleanupSync; LOG(StorageAPI, "DatabaseThread %p was asked to terminate\n", this); -#if PLATFORM(IOS) - m_pausedQueue.kill(); -#endif m_queue.kill(); } @@ -104,99 +93,18 @@ void DatabaseThread::databaseThreadStart(void* vDatabaseThread) dbThread->databaseThread(); } -#if PLATFORM(IOS) -class DatabaseUnpauseTask : public DatabaseTask { -public: - static std::unique_ptr<DatabaseUnpauseTask> create(DatabaseThread* thread) - { - return std::unique_ptr<DatabaseUnpauseTask>(new DatabaseUnpauseTask(thread)); - } - - virtual bool shouldPerformWhilePaused() const - { - // Since we're not locking the DatabaseThread::m_paused in the main database thread loop, it's possible that - // a DatabaseUnpauseTask might be added to the m_pausedQueue and performed from within ::handlePausedQueue. - // To protect against this, we allow it to be performed even if the database is paused. - // If the thread is paused when it is being performed, the tasks from the paused queue will simply be - // requeued instead of performed. - return true; - } - -private: - DatabaseUnpauseTask(DatabaseThread* thread) - : DatabaseTask(0, 0) - , m_thread(thread) - {} - - virtual void doPerformTask() - { - m_thread->handlePausedQueue(); - } -#if !LOG_DISABLED - virtual const char* debugTaskName() const { return "DatabaseUnpauseTask"; } -#endif - - DatabaseThread* m_thread; -}; - - -void DatabaseThread::setPaused(bool paused) -{ - if (m_paused == paused) - return; - - MutexLocker pausedLocker(m_pausedMutex); - m_paused = paused; - if (!m_paused) - scheduleTask(DatabaseUnpauseTask::create(this)); -} - -void DatabaseThread::handlePausedQueue() -{ - Vector<std::unique_ptr<DatabaseTask> > pausedTasks; - while (auto task = m_pausedQueue.tryGetMessage()) - pausedTasks.append(std::move(task)); - - for (unsigned i = 0; i < pausedTasks.size(); ++i) { - AutodrainedPool pool; - - std::unique_ptr<DatabaseTask> task(pausedTasks[i].release()); - { - MutexLocker pausedLocker(m_pausedMutex); - if (m_paused) { - m_pausedQueue.append(std::move(task)); - continue; - } - } - - if (terminationRequested()) - break; - - task->performTask(); - } -} -#endif //PLATFORM(IOS) - - void DatabaseThread::databaseThread() { { // Wait for DatabaseThread::start() to complete. - MutexLocker lock(m_threadCreationMutex); + LockHolder lock(m_threadCreationMutex); LOG(StorageAPI, "Started DatabaseThread %p", this); } while (auto task = m_queue.waitForMessage()) { AutodrainedPool pool; -#if PLATFORM(IOS) - if (!m_paused || task->shouldPerformWhilePaused()) - task->performTask(); - else - m_pausedQueue.append(std::move(task)); -#else task->performTask(); -#endif } // Clean up the list of all pending transactions on this database thread @@ -206,69 +114,76 @@ void DatabaseThread::databaseThread() // Close the databases that we ran transactions on. This ensures that if any transactions are still open, they are rolled back and we don't leave the database in an // inconsistent or locked state. - if (m_openDatabaseSet.size() > 0) { - // As the call to close will modify the original set, we must take a copy to iterate over. - DatabaseSet openSetCopy; - openSetCopy.swap(m_openDatabaseSet); - DatabaseSet::iterator end = openSetCopy.end(); - for (DatabaseSet::iterator it = openSetCopy.begin(); it != end; ++it) - (*it).get()->close(); + DatabaseSet openSetCopy; + { + LockHolder lock(m_openDatabaseSetMutex); + if (m_openDatabaseSet.size() > 0) { + // As the call to close will modify the original set, we must take a copy to iterate over. + openSetCopy.swap(m_openDatabaseSet); + } } + for (auto& openDatabase : openSetCopy) + openDatabase->performClose(); + // Detach the thread so its resources are no longer of any concern to anyone else detachThread(m_threadID); DatabaseTaskSynchronizer* cleanupSync = m_cleanupSync; // Clear the self refptr, possibly resulting in deletion - m_selfRef = 0; + m_selfRef = nullptr; if (cleanupSync) // Someone wanted to know when we were done cleaning up. cleanupSync->taskCompleted(); } -void DatabaseThread::recordDatabaseOpen(DatabaseBackend* database) +void DatabaseThread::recordDatabaseOpen(Database& database) { + LockHolder lock(m_openDatabaseSetMutex); + ASSERT(currentThread() == m_threadID); - ASSERT(database); - ASSERT(!m_openDatabaseSet.contains(database)); - m_openDatabaseSet.add(database); + ASSERT(!m_openDatabaseSet.contains(&database)); + m_openDatabaseSet.add(&database); } -void DatabaseThread::recordDatabaseClosed(DatabaseBackend* database) +void DatabaseThread::recordDatabaseClosed(Database& database) { + LockHolder lock(m_openDatabaseSetMutex); + ASSERT(currentThread() == m_threadID); - ASSERT(database); - ASSERT(m_queue.killed() || m_openDatabaseSet.contains(database)); - m_openDatabaseSet.remove(database); + ASSERT(m_queue.killed() || m_openDatabaseSet.contains(&database)); + m_openDatabaseSet.remove(&database); } -void DatabaseThread::scheduleTask(std::unique_ptr<DatabaseTask> task) +void DatabaseThread::scheduleTask(std::unique_ptr<DatabaseTask>&& task) { ASSERT(!task->hasSynchronizer() || task->hasCheckedForTermination()); - m_queue.append(std::move(task)); + m_queue.append(WTFMove(task)); } -void DatabaseThread::scheduleImmediateTask(std::unique_ptr<DatabaseTask> task) +void DatabaseThread::scheduleImmediateTask(std::unique_ptr<DatabaseTask>&& task) { ASSERT(!task->hasSynchronizer() || task->hasCheckedForTermination()); - m_queue.prepend(std::move(task)); + m_queue.prepend(WTFMove(task)); } -class SameDatabasePredicate { -public: - SameDatabasePredicate(const DatabaseBackend* database) : m_database(database) { } - bool operator()(const DatabaseTask& task) const { return task.database() == m_database; } -private: - const DatabaseBackend* m_database; -}; +void DatabaseThread::unscheduleDatabaseTasks(Database& database) +{ + // The thread loop is running, sp some tasks for this database may still be executed. This is unavoidable. + m_queue.removeIf([&database] (const DatabaseTask& task) { + return &task.database() == &database; + }); +} -void DatabaseThread::unscheduleDatabaseTasks(DatabaseBackend* database) +bool DatabaseThread::hasPendingDatabaseActivity() const { - // Note that the thread loop is running, so some tasks for the database - // may still be executed. This is unavoidable. - SameDatabasePredicate predicate(database); - m_queue.removeIf(predicate); + LockHolder lock(m_openDatabaseSetMutex); + for (auto& database : m_openDatabaseSet) { + if (database->hasPendingCreationEvent() || database->hasPendingTransaction()) + return true; + } + return false; } + } // namespace WebCore -#endif diff --git a/Source/WebCore/Modules/webdatabase/DatabaseThread.h b/Source/WebCore/Modules/webdatabase/DatabaseThread.h index c05979e5a..ec041e318 100644 --- a/Source/WebCore/Modules/webdatabase/DatabaseThread.h +++ b/Source/WebCore/Modules/webdatabase/DatabaseThread.h @@ -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. * @@ -25,82 +25,62 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DatabaseThread_h -#define DatabaseThread_h -#if ENABLE(SQL_DATABASE) +#pragma once -#include <wtf/Deque.h> -#include <wtf/HashMap.h> +#include <memory> #include <wtf/HashSet.h> #include <wtf/MessageQueue.h> -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> -#include <wtf/PassRefPtr.h> #include <wtf/RefPtr.h> #include <wtf/Threading.h> namespace WebCore { -class DatabaseBackend; +class Database; class DatabaseTask; class DatabaseTaskSynchronizer; class Document; -class SQLTransactionClient; class SQLTransactionCoordinator; class DatabaseThread : public ThreadSafeRefCounted<DatabaseThread> { public: - static PassRefPtr<DatabaseThread> create() { return adoptRef(new DatabaseThread); } + static Ref<DatabaseThread> create() { return adoptRef(*new DatabaseThread); } ~DatabaseThread(); bool start(); void requestTermination(DatabaseTaskSynchronizer* cleanupSync); - bool terminationRequested(DatabaseTaskSynchronizer* taskSynchronizer = 0) const; + bool terminationRequested(DatabaseTaskSynchronizer* = nullptr) const; - void scheduleTask(std::unique_ptr<DatabaseTask>); - void scheduleImmediateTask(std::unique_ptr<DatabaseTask>); // This just adds the task to the front of the queue - the caller needs to be extremely careful not to create deadlocks when waiting for completion. - void unscheduleDatabaseTasks(DatabaseBackend*); + void scheduleTask(std::unique_ptr<DatabaseTask>&&); + void scheduleImmediateTask(std::unique_ptr<DatabaseTask>&&); // This just adds the task to the front of the queue - the caller needs to be extremely careful not to create deadlocks when waiting for completion. + void unscheduleDatabaseTasks(Database&); + bool hasPendingDatabaseActivity() const; - void recordDatabaseOpen(DatabaseBackend*); - void recordDatabaseClosed(DatabaseBackend*); + void recordDatabaseOpen(Database&); + void recordDatabaseClosed(Database&); ThreadIdentifier getThreadID() { return m_threadID; } - SQLTransactionClient* transactionClient() { return m_transactionClient.get(); } SQLTransactionCoordinator* transactionCoordinator() { return m_transactionCoordinator.get(); } -#if PLATFORM(IOS) - void setPaused(bool); - void handlePausedQueue(); -#endif - private: DatabaseThread(); static void databaseThreadStart(void*); void databaseThread(); - Mutex m_threadCreationMutex; - ThreadIdentifier m_threadID; + Lock m_threadCreationMutex; + ThreadIdentifier m_threadID { 0 }; RefPtr<DatabaseThread> m_selfRef; MessageQueue<DatabaseTask> m_queue; -#if PLATFORM(IOS) - MessageQueue<DatabaseTask> m_pausedQueue; - Mutex m_pausedMutex; - volatile bool m_paused; -#endif // This set keeps track of the open databases that have been used on this thread. - typedef HashSet<RefPtr<DatabaseBackend>> DatabaseSet; + using DatabaseSet = HashSet<RefPtr<Database>>; + mutable Lock m_openDatabaseSetMutex; DatabaseSet m_openDatabaseSet; - OwnPtr<SQLTransactionClient> m_transactionClient; - OwnPtr<SQLTransactionCoordinator> m_transactionCoordinator; - DatabaseTaskSynchronizer* m_cleanupSync; + std::unique_ptr<SQLTransactionCoordinator> m_transactionCoordinator; + DatabaseTaskSynchronizer* m_cleanupSync { nullptr }; }; } // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) -#endif // DatabaseThread_h 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 diff --git a/Source/WebCore/Modules/webdatabase/DatabaseTracker.h b/Source/WebCore/Modules/webdatabase/DatabaseTracker.h index acea6988f..af6cb5bf3 100644 --- a/Source/WebCore/Modules/webdatabase/DatabaseTracker.h +++ b/Source/WebCore/Modules/webdatabase/DatabaseTracker.h @@ -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. * @@ -26,35 +26,38 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DatabaseTracker_h -#define DatabaseTracker_h - -#if ENABLE(SQL_DATABASE) +#pragma once #include "DatabaseDetails.h" -#include "DatabaseError.h" +#include "ExceptionOr.h" #include "SQLiteDatabase.h" +#include "SecurityOriginData.h" #include "SecurityOriginHash.h" +#include <wtf/HashCountedSet.h> #include <wtf/HashMap.h> #include <wtf/HashSet.h> -#include <wtf/OwnPtr.h> #include <wtf/text/StringHash.h> -#include <wtf/text/WTFString.h> namespace WebCore { -class DatabaseBackendBase; -class DatabaseBackendContext; +class Database; +class DatabaseContext; class DatabaseManagerClient; class OriginLock; class SecurityOrigin; +struct SecurityOriginData; + +enum class CurrentQueryBehavior { Interrupt, RunToCompletion }; class DatabaseTracker { WTF_MAKE_NONCOPYABLE(DatabaseTracker); WTF_MAKE_FAST_ALLOCATED; public: + // FIXME: This is a hack so we can easily delete databases from the UI process in WebKit2. + WEBCORE_EXPORT static std::unique_ptr<DatabaseTracker> trackerWithDatabasePath(const String& databasePath); + static void initializeTracker(const String& databasePath); - static DatabaseTracker& tracker(); + WEBCORE_EXPORT static DatabaseTracker& singleton(); // This singleton will potentially be used from multiple worker threads and the page's context thread simultaneously. To keep this safe, it's // currently using 4 locks. In order to avoid deadlock when taking multiple locks, you must take them in the correct order: // m_databaseGuard before quotaManager if both locks are needed. @@ -62,72 +65,63 @@ public: // m_databaseGuard and m_openDatabaseMapGuard currently don't overlap. // notificationMutex() is currently independent of the other locks. - bool canEstablishDatabase(DatabaseBackendContext*, const String& name, unsigned long estimatedSize, DatabaseError&); - bool retryCanEstablishDatabase(DatabaseBackendContext*, const String& name, unsigned long estimatedSize, DatabaseError&); - - void setDatabaseDetails(SecurityOrigin*, const String& name, const String& displayName, unsigned long estimatedSize); - String fullPathForDatabase(SecurityOrigin*, const String& name, bool createIfDoesNotExist = true); - - void addOpenDatabase(DatabaseBackendBase*); - void removeOpenDatabase(DatabaseBackendBase*); - void getOpenDatabases(SecurityOrigin*, const String& name, HashSet<RefPtr<DatabaseBackendBase>>* databases); + ExceptionOr<void> canEstablishDatabase(DatabaseContext&, const String& name, unsigned estimatedSize); + ExceptionOr<void> retryCanEstablishDatabase(DatabaseContext&, const String& name, unsigned estimatedSize); - unsigned long long getMaxSizeForDatabase(const DatabaseBackendBase*); + void setDatabaseDetails(const SecurityOriginData&, const String& name, const String& displayName, unsigned estimatedSize); + String fullPathForDatabase(const SecurityOriginData&, const String& name, bool createIfDoesNotExist); - void interruptAllDatabasesForContext(const DatabaseBackendContext*); + void addOpenDatabase(Database&); + void removeOpenDatabase(Database&); -private: - explicit DatabaseTracker(const String& databasePath); + unsigned long long maximumSize(Database&); - bool hasAdequateQuotaForOrigin(SecurityOrigin*, unsigned long estimatedSize, DatabaseError&); + WEBCORE_EXPORT void closeAllDatabases(CurrentQueryBehavior = CurrentQueryBehavior::RunToCompletion); -public: - void setDatabaseDirectoryPath(const String&); - String databaseDirectoryPath() const; + WEBCORE_EXPORT Vector<SecurityOriginData> origins(); + WEBCORE_EXPORT Vector<String> databaseNames(const SecurityOriginData&); - void origins(Vector<RefPtr<SecurityOrigin>>& result); - bool databaseNamesForOrigin(SecurityOrigin*, Vector<String>& result); + DatabaseDetails detailsForNameAndOrigin(const String&, const SecurityOriginData&); - DatabaseDetails detailsForNameAndOrigin(const String&, SecurityOrigin*); + WEBCORE_EXPORT unsigned long long usage(const SecurityOriginData&); + WEBCORE_EXPORT unsigned long long quota(const SecurityOriginData&); + WEBCORE_EXPORT void setQuota(const SecurityOriginData&, unsigned long long); + RefPtr<OriginLock> originLockFor(const SecurityOriginData&); - unsigned long long usageForOrigin(SecurityOrigin*); - unsigned long long quotaForOrigin(SecurityOrigin*); - void setQuota(SecurityOrigin*, unsigned long long); - PassRefPtr<OriginLock> originLockFor(SecurityOrigin*); - - void deleteAllDatabases(); - bool deleteOrigin(SecurityOrigin*); - bool deleteDatabase(SecurityOrigin*, const String& name); + WEBCORE_EXPORT void deleteAllDatabasesImmediately(); + WEBCORE_EXPORT void deleteDatabasesModifiedSince(std::chrono::system_clock::time_point); + WEBCORE_EXPORT bool deleteOrigin(const SecurityOriginData&); + WEBCORE_EXPORT bool deleteDatabase(const SecurityOriginData&, const String& name); #if PLATFORM(IOS) - void removeDeletedOpenedDatabases(); - static bool deleteDatabaseFileIfEmpty(const String&); + WEBCORE_EXPORT void removeDeletedOpenedDatabases(); + WEBCORE_EXPORT static bool deleteDatabaseFileIfEmpty(const String&); // MobileSafari will grab this mutex on the main thread before dispatching the task to // clean up zero byte database files. Any operations to open new database will have to // wait for that task to finish by waiting on this mutex. - static Mutex& openDatabaseMutex(); - - static void emptyDatabaseFilesRemovalTaskWillBeScheduled(); - static void emptyDatabaseFilesRemovalTaskDidFinish(); + static Lock& openDatabaseMutex(); - void setDatabasesPaused(bool); + WEBCORE_EXPORT static void emptyDatabaseFilesRemovalTaskWillBeScheduled(); + WEBCORE_EXPORT static void emptyDatabaseFilesRemovalTaskDidFinish(); #endif void setClient(DatabaseManagerClient*); // From a secondary thread, must be thread safe with its data - void scheduleNotifyDatabaseChanged(SecurityOrigin*, const String& name); + void scheduleNotifyDatabaseChanged(const SecurityOriginData&, const String& name); - bool hasEntryForOrigin(SecurityOrigin*); - - void doneCreatingDatabase(DatabaseBackendBase*); + void doneCreatingDatabase(Database&); private: - bool hasEntryForOriginNoLock(SecurityOrigin* origin); - String fullPathForDatabaseNoLock(SecurityOrigin*, const String& name, bool createIfDoesNotExist); - bool databaseNamesForOriginNoLock(SecurityOrigin* origin, Vector<String>& resultVector); - unsigned long long quotaForOriginNoLock(SecurityOrigin* origin); + explicit DatabaseTracker(const String& databasePath); + + ExceptionOr<void> hasAdequateQuotaForOrigin(const SecurityOriginData&, unsigned estimatedSize); + + bool hasEntryForOriginNoLock(const SecurityOriginData&); + String fullPathForDatabaseNoLock(const SecurityOriginData&, const String& name, bool createIfDoesNotExist); + Vector<String> databaseNamesNoLock(const SecurityOriginData&); + unsigned long long quotaNoLock(const SecurityOriginData&); String trackerDatabasePath() const; @@ -137,59 +131,65 @@ private: }; void openTrackerDatabase(TrackerCreationAction); - String originPath(SecurityOrigin*) const; + String originPath(const SecurityOriginData&) const; + + bool hasEntryForDatabase(const SecurityOriginData&, const String& databaseIdentifier); - bool hasEntryForDatabase(SecurityOrigin*, const String& databaseIdentifier); + bool addDatabase(const SecurityOriginData&, const String& name, const String& path); - bool addDatabase(SecurityOrigin*, const String& name, const String& path); + enum class DeletionMode { + Immediate, +#if PLATFORM(IOS) + // Deferred deletion is currently only supported on iOS + // (see removeDeletedOpenedDatabases etc, above). + Deferred, + Default = Deferred +#else + Default = Immediate +#endif + }; - bool deleteDatabaseFile(SecurityOrigin*, const String& name); + bool deleteOrigin(const SecurityOriginData&, DeletionMode); + bool deleteDatabaseFile(const SecurityOriginData&, const String& name, DeletionMode); - void deleteOriginLockFor(SecurityOrigin*); + void deleteOriginLockFor(const SecurityOriginData&); - typedef HashSet<DatabaseBackendBase*> DatabaseSet; - typedef HashMap<String, DatabaseSet*> DatabaseNameMap; - typedef HashMap<RefPtr<SecurityOrigin>, DatabaseNameMap*> DatabaseOriginMap; + using DatabaseSet = HashSet<Database*>; + using DatabaseNameMap = HashMap<String, DatabaseSet*>; + using DatabaseOriginMap = HashMap<SecurityOriginData, DatabaseNameMap*>; - Mutex m_openDatabaseMapGuard; - mutable OwnPtr<DatabaseOriginMap> m_openDatabaseMap; + Lock m_openDatabaseMapGuard; + mutable std::unique_ptr<DatabaseOriginMap> m_openDatabaseMap; // This lock protects m_database, m_originLockMap, m_databaseDirectoryPath, m_originsBeingDeleted, m_beingCreated, and m_beingDeleted. - Mutex m_databaseGuard; + Lock m_databaseGuard; SQLiteDatabase m_database; - typedef HashMap<String, RefPtr<OriginLock>> OriginLockMap; + using OriginLockMap = HashMap<String, RefPtr<OriginLock>>; OriginLockMap m_originLockMap; String m_databaseDirectoryPath; - DatabaseManagerClient* m_client; - - typedef HashMap<String, long> NameCountMap; - typedef HashMap<RefPtr<SecurityOrigin>, NameCountMap*, SecurityOriginHash> CreateSet; - CreateSet m_beingCreated; - typedef HashSet<String> NameSet; - HashMap<RefPtr<SecurityOrigin>, NameSet*> m_beingDeleted; - HashSet<RefPtr<SecurityOrigin>> m_originsBeingDeleted; - bool isDeletingDatabaseOrOriginFor(SecurityOrigin*, const String& name); - void recordCreatingDatabase(SecurityOrigin*, const String& name); - void doneCreatingDatabase(SecurityOrigin*, const String& name); - bool creatingDatabase(SecurityOrigin*, const String& name); - bool canDeleteDatabase(SecurityOrigin*, const String& name); - void recordDeletingDatabase(SecurityOrigin*, const String& name); - void doneDeletingDatabase(SecurityOrigin*, const String& name); - bool isDeletingDatabase(SecurityOrigin*, const String& name); - bool canDeleteOrigin(SecurityOrigin*); - bool isDeletingOrigin(SecurityOrigin*); - void recordDeletingOrigin(SecurityOrigin*); - void doneDeletingOrigin(SecurityOrigin*); + DatabaseManagerClient* m_client { nullptr }; + + HashMap<SecurityOriginData, std::unique_ptr<HashCountedSet<String>>> m_beingCreated; + HashMap<SecurityOriginData, std::unique_ptr<HashSet<String>>> m_beingDeleted; + HashSet<SecurityOriginData> m_originsBeingDeleted; + bool isDeletingDatabaseOrOriginFor(const SecurityOriginData&, const String& name); + void recordCreatingDatabase(const SecurityOriginData&, const String& name); + void doneCreatingDatabase(const SecurityOriginData&, const String& name); + bool creatingDatabase(const SecurityOriginData&, const String& name); + bool canDeleteDatabase(const SecurityOriginData&, const String& name); + void recordDeletingDatabase(const SecurityOriginData&, const String& name); + void doneDeletingDatabase(const SecurityOriginData&, const String& name); + bool isDeletingDatabase(const SecurityOriginData&, const String& name); + bool canDeleteOrigin(const SecurityOriginData&); + bool isDeletingOrigin(const SecurityOriginData&); + void recordDeletingOrigin(const SecurityOriginData&); + void doneDeletingOrigin(const SecurityOriginData&); static void scheduleForNotification(); - static void notifyDatabasesChanged(void*); + static void notifyDatabasesChanged(); }; } // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) - -#endif // DatabaseTracker_h diff --git a/Source/WebCore/Modules/webdatabase/OriginLock.cpp b/Source/WebCore/Modules/webdatabase/OriginLock.cpp index 7361e3663..138025ac3 100644 --- a/Source/WebCore/Modules/webdatabase/OriginLock.cpp +++ b/Source/WebCore/Modules/webdatabase/OriginLock.cpp @@ -26,10 +26,7 @@ #include "config.h" #include "OriginLock.h" -#if ENABLE(SQL_DATABASE) - #include "FileSystem.h" -#include <wtf/PassOwnPtr.h> namespace WebCore { @@ -97,5 +94,3 @@ void OriginLock::deleteLockFile(String originPath) } } // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) diff --git a/Source/WebCore/Modules/webdatabase/OriginLock.h b/Source/WebCore/Modules/webdatabase/OriginLock.h index 791a56a4a..d7151de78 100644 --- a/Source/WebCore/Modules/webdatabase/OriginLock.h +++ b/Source/WebCore/Modules/webdatabase/OriginLock.h @@ -23,14 +23,11 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef OriginLock_h -#define OriginLock_h - -#if ENABLE(SQL_DATABASE) +#pragma once #include "FileSystem.h" +#include <wtf/Lock.h> #include <wtf/ThreadSafeRefCounted.h> -#include <wtf/ThreadingPrimitives.h> #include <wtf/text/WTFString.h> namespace WebCore { @@ -39,7 +36,7 @@ class OriginLock : public ThreadSafeRefCounted<OriginLock> { WTF_MAKE_NONCOPYABLE(OriginLock); WTF_MAKE_FAST_ALLOCATED; public: OriginLock(String originPath); - ~OriginLock(); + WEBCORE_EXPORT ~OriginLock(); void lock(); void unlock(); @@ -50,14 +47,10 @@ private: static String lockFileNameForPath(String originPath); String m_lockFileName; - Mutex m_mutex; + Lock m_mutex; #if USE(FILE_LOCK) PlatformFileHandle m_lockHandle; #endif }; } // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) - -#endif // OriginLock_h diff --git a/Source/WebCore/Modules/webdatabase/SQLCallbackWrapper.h b/Source/WebCore/Modules/webdatabase/SQLCallbackWrapper.h index c184a3738..d0d128096 100644 --- a/Source/WebCore/Modules/webdatabase/SQLCallbackWrapper.h +++ b/Source/WebCore/Modules/webdatabase/SQLCallbackWrapper.h @@ -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. * @@ -25,13 +25,11 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SQLCallbackWrapper_h -#define SQLCallbackWrapper_h -#if ENABLE(SQL_DATABASE) +#pragma once #include "ScriptExecutionContext.h" -#include <wtf/ThreadingPrimitives.h> +#include <wtf/Lock.h> namespace WebCore { @@ -43,8 +41,8 @@ namespace WebCore { // - by unwrapping and then dereferencing normally - on context thread only template<typename T> class SQLCallbackWrapper { public: - SQLCallbackWrapper(PassRefPtr<T> callback, ScriptExecutionContext* scriptExecutionContext) - : m_callback(callback) + SQLCallbackWrapper(RefPtr<T>&& callback, ScriptExecutionContext* scriptExecutionContext) + : m_callback(WTFMove(callback)) , m_scriptExecutionContext(m_callback ? scriptExecutionContext : 0) { ASSERT(!m_callback || (m_scriptExecutionContext.get() && m_scriptExecutionContext->isContextThread())); @@ -57,69 +55,47 @@ public: void clear() { - ScriptExecutionContext* context; + ScriptExecutionContext* scriptExecutionContextPtr; T* callback; { - MutexLocker locker(m_mutex); + LockHolder locker(m_mutex); if (!m_callback) { ASSERT(!m_scriptExecutionContext); return; } if (m_scriptExecutionContext->isContextThread()) { - m_callback = 0; - m_scriptExecutionContext = 0; + m_callback = nullptr; + m_scriptExecutionContext = nullptr; return; } - context = m_scriptExecutionContext.release().leakRef(); - callback = m_callback.release().leakRef(); + scriptExecutionContextPtr = m_scriptExecutionContext.leakRef(); + callback = m_callback.leakRef(); } - context->postTask(SafeReleaseTask::create(callback)); + scriptExecutionContextPtr->postTask({ + ScriptExecutionContext::Task::CleanupTask, + [callback, scriptExecutionContextPtr] (ScriptExecutionContext& context) { + ASSERT_UNUSED(context, &context == scriptExecutionContextPtr && context.isContextThread()); + callback->deref(); + scriptExecutionContextPtr->deref(); + } + }); } - PassRefPtr<T> unwrap() + RefPtr<T> unwrap() { - MutexLocker locker(m_mutex); + LockHolder locker(m_mutex); ASSERT(!m_callback || m_scriptExecutionContext->isContextThread()); - m_scriptExecutionContext = 0; - return m_callback.release(); + m_scriptExecutionContext = nullptr; + return WTFMove(m_callback); } // Useful for optimizations only, please test the return value of unwrap to be sure. bool hasCallback() const { return m_callback; } private: - class SafeReleaseTask : public ScriptExecutionContext::Task { - public: - static PassOwnPtr<SafeReleaseTask> create(T* callbackToRelease) - { - return adoptPtr(new SafeReleaseTask(callbackToRelease)); - } - - virtual void performTask(ScriptExecutionContext* context) - { - ASSERT(m_callbackToRelease && context && context->isContextThread()); - m_callbackToRelease->deref(); - context->deref(); - } - - virtual bool isCleanupTask() const { return true; } - - private: - explicit SafeReleaseTask(T* callbackToRelease) - : m_callbackToRelease(callbackToRelease) - { - } - - T* m_callbackToRelease; - }; - - Mutex m_mutex; + Lock m_mutex; RefPtr<T> m_callback; RefPtr<ScriptExecutionContext> m_scriptExecutionContext; }; } // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) - -#endif // SQLCallbackWrapper_h diff --git a/Source/WebCore/Modules/webdatabase/SQLError.h b/Source/WebCore/Modules/webdatabase/SQLError.h index fd423abdb..4e276ca78 100644 --- a/Source/WebCore/Modules/webdatabase/SQLError.h +++ b/Source/WebCore/Modules/webdatabase/SQLError.h @@ -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. * @@ -26,10 +26,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SQLError_h -#define SQLError_h - -#if ENABLE(SQL_DATABASE) +#pragma once #include <wtf/ThreadSafeRefCounted.h> #include <wtf/text/WTFString.h> @@ -38,12 +35,12 @@ namespace WebCore { class SQLError : public ThreadSafeRefCounted<SQLError> { public: - static PassRefPtr<SQLError> create(unsigned code, const String& message) { return adoptRef(new SQLError(code, message)); } - static PassRefPtr<SQLError> create(unsigned code, const char* message, int sqliteCode) + static Ref<SQLError> create(unsigned code, const String& message) { return adoptRef(*new SQLError(code, message)); } + static Ref<SQLError> create(unsigned code, const char* message, int sqliteCode) { return create(code, String::format("%s (%d)", message, sqliteCode)); } - static PassRefPtr<SQLError> create(unsigned code, const char* message, int sqliteCode, const char* sqliteMessage) + static Ref<SQLError> create(unsigned code, const char* message, int sqliteCode, const char* sqliteMessage) { return create(code, String::format("%s (%d %s)", message, sqliteCode, sqliteMessage)); } @@ -68,8 +65,4 @@ private: String m_message; }; -} - -#endif - -#endif // SQLError_h +} // namespace WebCore diff --git a/Source/WebCore/Modules/webdatabase/SQLError.idl b/Source/WebCore/Modules/webdatabase/SQLError.idl index 8187f960e..b4e5db7b6 100644 --- a/Source/WebCore/Modules/webdatabase/SQLError.idl +++ b/Source/WebCore/Modules/webdatabase/SQLError.idl @@ -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. * @@ -27,9 +27,6 @@ */ [ - NoInterfaceObject, - Conditional=SQL_DATABASE, - JSNoStaticTables, ImplementationLacksVTable ] interface SQLError { readonly attribute unsigned long code; diff --git a/Source/WebCore/Modules/webdatabase/SQLException.cpp b/Source/WebCore/Modules/webdatabase/SQLException.cpp index e70d71f53..e9f880edc 100644 --- a/Source/WebCore/Modules/webdatabase/SQLException.cpp +++ b/Source/WebCore/Modules/webdatabase/SQLException.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. * @@ -27,14 +27,13 @@ */ #include "config.h" - -#if ENABLE(SQL_DATABASE) - #include "SQLException.h" +#include "ExceptionCodeDescription.h" + namespace WebCore { -static struct SQLExceptionNameDescription { +static const struct SQLExceptionNameDescription { const char* const name; const char* const description; } sqlExceptions[] = { @@ -67,5 +66,3 @@ bool SQLException::initializeDescription(ExceptionCode ec, ExceptionCodeDescript } } // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) diff --git a/Source/WebCore/Modules/webdatabase/SQLException.h b/Source/WebCore/Modules/webdatabase/SQLException.h index 7ea09982a..281a85e2b 100644 --- a/Source/WebCore/Modules/webdatabase/SQLException.h +++ b/Source/WebCore/Modules/webdatabase/SQLException.h @@ -28,10 +28,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SQLException_h -#define SQLException_h - -#if ENABLE(SQL_DATABASE) +#pragma once #include "ExceptionBase.h" @@ -39,9 +36,9 @@ namespace WebCore { class SQLException : public ExceptionBase { public: - static PassRefPtr<SQLException> create(const ExceptionCodeDescription& description) + static Ref<SQLException> create(const ExceptionCodeDescription& description) { - return adoptRef(new SQLException(description)); + return adoptRef(*new SQLException(description)); } static const int SQLExceptionOffset = 1000; @@ -68,7 +65,3 @@ private: }; } // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) - -#endif // SQLException_h diff --git a/Source/WebCore/Modules/webdatabase/SQLException.idl b/Source/WebCore/Modules/webdatabase/SQLException.idl index b9511b4dd..3dc160a8f 100644 --- a/Source/WebCore/Modules/webdatabase/SQLException.idl +++ b/Source/WebCore/Modules/webdatabase/SQLException.idl @@ -29,8 +29,6 @@ */ [ - Conditional=SQL_DATABASE, - JSNoStaticTables, DoNotCheckConstants, ImplementationLacksVTable ] exception SQLException { diff --git a/Source/WebCore/Modules/webdatabase/SQLResultSet.cpp b/Source/WebCore/Modules/webdatabase/SQLResultSet.cpp index 91a2d9108..da63f2b90 100644 --- a/Source/WebCore/Modules/webdatabase/SQLResultSet.cpp +++ b/Source/WebCore/Modules/webdatabase/SQLResultSet.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,54 +29,22 @@ #include "config.h" #include "SQLResultSet.h" -#if ENABLE(SQL_DATABASE) - #include "ExceptionCode.h" namespace WebCore { SQLResultSet::SQLResultSet() : m_rows(SQLResultSetRowList::create()) - , m_insertId(0) - , m_insertIdSet(false) - , m_rowsAffected(0) { } -int64_t SQLResultSet::insertId(ExceptionCode& e) const +ExceptionOr<int64_t> SQLResultSet::insertId() const { // 4.11.4 - Return the id of the last row inserted as a result of the query // If the query didn't result in any rows being added, raise an INVALID_ACCESS_ERR exception - if (m_insertIdSet) - return m_insertId; - - e = INVALID_ACCESS_ERR; - return -1; -} - -int SQLResultSet::rowsAffected() const -{ - return m_rowsAffected; -} - -SQLResultSetRowList* SQLResultSet::rows() const -{ - return m_rows.get(); -} - -void SQLResultSet::setInsertId(int64_t id) -{ - ASSERT(!m_insertIdSet); - - m_insertId = id; - m_insertIdSet = true; -} - -void SQLResultSet::setRowsAffected(int count) -{ - m_rowsAffected = count; + if (!m_insertId) + return Exception { INVALID_ACCESS_ERR }; + return m_insertId.value(); } } - -#endif diff --git a/Source/WebCore/Modules/webdatabase/SQLResultSet.h b/Source/WebCore/Modules/webdatabase/SQLResultSet.h index 28ea43178..e2283999f 100644 --- a/Source/WebCore/Modules/webdatabase/SQLResultSet.h +++ b/Source/WebCore/Modules/webdatabase/SQLResultSet.h @@ -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. * @@ -26,12 +26,9 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SQLResultSet_h -#define SQLResultSet_h +#pragma once -#if ENABLE(SQL_DATABASE) - -#include "DatabaseBasicTypes.h" +#include "ExceptionOr.h" #include "SQLResultSetRowList.h" #include <wtf/ThreadSafeRefCounted.h> @@ -39,28 +36,33 @@ namespace WebCore { class SQLResultSet : public ThreadSafeRefCounted<SQLResultSet> { public: - static PassRefPtr<SQLResultSet> create() { return adoptRef(new SQLResultSet); } + static Ref<SQLResultSet> create() { return adoptRef(*new SQLResultSet); } - SQLResultSetRowList* rows() const; + SQLResultSetRowList& rows() { return m_rows.get(); } - int64_t insertId(ExceptionCode&) const; - int rowsAffected() const; + ExceptionOr<int64_t> insertId() const; + int rowsAffected() const { return m_rowsAffected; } -// For internal (non-JS) use void setInsertId(int64_t); void setRowsAffected(int); private: SQLResultSet(); - RefPtr<SQLResultSetRowList> m_rows; - int64_t m_insertId; - bool m_insertIdSet; - int m_rowsAffected; + Ref<SQLResultSetRowList> m_rows; + std::optional<int64_t> m_insertId; + int m_rowsAffected { 0 }; }; -} // namespace WebCore +inline void SQLResultSet::setInsertId(int64_t id) +{ + ASSERT(!m_insertId); + m_insertId = id; +} -#endif +inline void SQLResultSet::setRowsAffected(int count) +{ + m_rowsAffected = count; +} -#endif // SQLResultSet_h +} // namespace WebCore diff --git a/Source/WebCore/Modules/webdatabase/SQLResultSet.idl b/Source/WebCore/Modules/webdatabase/SQLResultSet.idl index d3b93e40d..a381e5813 100644 --- a/Source/WebCore/Modules/webdatabase/SQLResultSet.idl +++ b/Source/WebCore/Modules/webdatabase/SQLResultSet.idl @@ -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. * @@ -27,18 +27,9 @@ */ [ - NoInterfaceObject, - Conditional=SQL_DATABASE, - JSNoStaticTables, ImplementationLacksVTable ] interface SQLResultSet { readonly attribute SQLResultSetRowList rows; - -#if !defined(LANGUAGE_CPP) || !LANGUAGE_CPP - [GetterRaisesException] readonly attribute long insertId; -#else - // Explicitely choose 'long long' here to avoid a 64bit->32bit shortening warning for us. - [GetterRaisesException] readonly attribute long long insertId; -#endif + [GetterMayThrowException] readonly attribute long long insertId; readonly attribute long rowsAffected; }; diff --git a/Source/WebCore/Modules/webdatabase/SQLResultSetRowList.cpp b/Source/WebCore/Modules/webdatabase/SQLResultSetRowList.cpp index c99b40a56..28dc3b36d 100644 --- a/Source/WebCore/Modules/webdatabase/SQLResultSetRowList.cpp +++ b/Source/WebCore/Modules/webdatabase/SQLResultSetRowList.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,13 +29,13 @@ #include "config.h" #include "SQLResultSetRowList.h" -#if ENABLE(SQL_DATABASE) +#include "ExceptionCode.h" namespace WebCore { unsigned SQLResultSetRowList::length() const { - if (m_result.size() == 0) + if (m_result.isEmpty()) return 0; ASSERT(m_result.size() % m_columns.size() == 0); @@ -43,6 +43,19 @@ unsigned SQLResultSetRowList::length() const return m_result.size() / m_columns.size(); } +ExceptionOr<Vector<WTF::KeyValuePair<String, SQLValue>>> SQLResultSetRowList::item(unsigned index) const +{ + if (index >= length()) + return Exception { INDEX_SIZE_ERR }; + + Vector<WTF::KeyValuePair<String, SQLValue>> result; + + unsigned numberOfColumns = m_columns.size(); + unsigned valuesIndex = index * numberOfColumns; + for (unsigned i = 0; i < numberOfColumns; i++) + result.append({ m_columns[i], m_result[valuesIndex + i] }); + + return WTFMove(result); } -#endif +} diff --git a/Source/WebCore/Modules/webdatabase/SQLResultSetRowList.h b/Source/WebCore/Modules/webdatabase/SQLResultSetRowList.h index 45d88b6ef..b6fd7980c 100644 --- a/Source/WebCore/Modules/webdatabase/SQLResultSetRowList.h +++ b/Source/WebCore/Modules/webdatabase/SQLResultSetRowList.h @@ -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. * @@ -26,20 +26,17 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SQLResultSetRowList_h -#define SQLResultSetRowList_h +#pragma once -#if ENABLE(SQL_DATABASE) - -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> +#include "ExceptionOr.h" #include "SQLValue.h" +#include <wtf/HashTraits.h> namespace WebCore { class SQLResultSetRowList : public RefCounted<SQLResultSetRowList> { public: - static PassRefPtr<SQLResultSetRowList> create() { return adoptRef(new SQLResultSetRowList); } + static Ref<SQLResultSetRowList> create() { return adoptRef(*new SQLResultSetRowList); } const Vector<String>& columnNames() const { return m_columns; } const Vector<SQLValue>& values() const { return m_result; } @@ -48,6 +45,7 @@ public: void addResult(const SQLValue& result) { m_result.append(result); } unsigned length() const; + ExceptionOr<Vector<WTF::KeyValuePair<String, SQLValue>>> item(unsigned index) const; private: SQLResultSetRowList() { } @@ -56,8 +54,4 @@ private: Vector<SQLValue> m_result; }; -} - -#endif - -#endif +} // namespace WebCore diff --git a/Source/WebCore/Modules/webdatabase/SQLResultSetRowList.idl b/Source/WebCore/Modules/webdatabase/SQLResultSetRowList.idl index 250a2aec7..5541c3559 100644 --- a/Source/WebCore/Modules/webdatabase/SQLResultSetRowList.idl +++ b/Source/WebCore/Modules/webdatabase/SQLResultSetRowList.idl @@ -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. * @@ -26,12 +26,11 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +typedef (DOMString? or unrestricted double) SQLValue; + [ - NoInterfaceObject, - Conditional=SQL_DATABASE, - JSNoStaticTables, ImplementationLacksVTable, ] interface SQLResultSetRowList { readonly attribute unsigned long length; - [Custom] any item(unsigned long index); + [MayThrowException] record<DOMString, SQLValue> item(unsigned long index); }; diff --git a/Source/WebCore/Modules/webdatabase/SQLStatement.cpp b/Source/WebCore/Modules/webdatabase/SQLStatement.cpp index 92c7f4f46..ea892431c 100644 --- a/Source/WebCore/Modules/webdatabase/SQLStatement.cpp +++ b/Source/WebCore/Modules/webdatabase/SQLStatement.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. * @@ -28,61 +28,186 @@ #include "config.h" #include "SQLStatement.h" -#if ENABLE(SQL_DATABASE) - -#include "AbstractDatabaseServer.h" -#include "AbstractSQLStatementBackend.h" #include "Database.h" -#include "DatabaseManager.h" #include "Logging.h" +#include "SQLError.h" +#include "SQLResultSet.h" #include "SQLStatementCallback.h" #include "SQLStatementErrorCallback.h" -#include "SQLTransaction.h" #include "SQLValue.h" #include "SQLiteDatabase.h" #include "SQLiteStatement.h" #include <wtf/text/CString.h> + +// The Life-Cycle of a SQLStatement i.e. Who's keeping the SQLStatement alive? +// ========================================================================== +// The RefPtr chain goes something like this: +// +// At birth (in SQLTransactionBackend::executeSQL()): +// ================================================= +// SQLTransactionBackend // Deque<RefPtr<SQLStatement>> m_statementQueue points to ... +// --> SQLStatement // std::unique_ptr<SQLStatement> m_frontend points to ... +// --> SQLStatement +// +// After grabbing the statement for execution (in SQLTransactionBackend::getNextStatement()): +// ========================================================================================= +// SQLTransactionBackend // RefPtr<SQLStatement> m_currentStatementBackend points to ... +// --> SQLStatement // std::unique_ptr<SQLStatement> m_frontend points to ... +// --> SQLStatement +// +// Then we execute the statement in SQLTransactionBackend::runCurrentStatementAndGetNextState(). +// And we callback to the script in SQLTransaction::deliverStatementCallback() if +// necessary. +// - Inside SQLTransaction::deliverStatementCallback(), we operate on a raw SQLStatement*. +// This pointer is valid because it is owned by SQLTransactionBackend's +// SQLTransactionBackend::m_currentStatementBackend. +// +// After we're done executing the statement (in SQLTransactionBackend::getNextStatement()): +// ======================================================================================= +// When we're done executing, we'll grab the next statement. But before we +// do that, getNextStatement() nullify SQLTransactionBackend::m_currentStatementBackend. +// This will trigger the deletion of the SQLStatement and SQLStatement. +// +// Note: unlike with SQLTransaction, there is no JS representation of SQLStatement. +// Hence, there is no GC dependency at play here. + namespace WebCore { -PassOwnPtr<SQLStatement> SQLStatement::create(Database* database, - PassRefPtr<SQLStatementCallback> callback, PassRefPtr<SQLStatementErrorCallback> errorCallback) +SQLStatement::SQLStatement(Database& database, const String& statement, Vector<SQLValue>&& arguments, RefPtr<SQLStatementCallback>&& callback, RefPtr<SQLStatementErrorCallback>&& errorCallback, int permissions) + : m_statement(statement.isolatedCopy()) + , m_arguments(WTFMove(arguments)) + , m_statementCallbackWrapper(WTFMove(callback), &database.scriptExecutionContext()) + , m_statementErrorCallbackWrapper(WTFMove(errorCallback), &database.scriptExecutionContext()) + , m_permissions(permissions) { - return adoptPtr(new SQLStatement(database, callback, errorCallback)); } -SQLStatement::SQLStatement(Database* database, PassRefPtr<SQLStatementCallback> callback, - PassRefPtr<SQLStatementErrorCallback> errorCallback) - : m_statementCallbackWrapper(callback, database->scriptExecutionContext()) - , m_statementErrorCallbackWrapper(errorCallback, database->scriptExecutionContext()) +SQLStatement::~SQLStatement() { } -void SQLStatement::setBackend(AbstractSQLStatementBackend* backend) +SQLError* SQLStatement::sqlError() const { - m_backend = backend; + return m_error.get(); } -bool SQLStatement::hasCallback() +SQLResultSet* SQLStatement::sqlResultSet() const { - return m_statementCallbackWrapper.hasCallback(); + return m_resultSet.get(); } -bool SQLStatement::hasErrorCallback() +bool SQLStatement::execute(Database& db) { - return m_statementErrorCallbackWrapper.hasCallback(); + ASSERT(!m_resultSet); + + // If we're re-running this statement after a quota violation, we need to clear that error now + clearFailureDueToQuota(); + + // This transaction might have been marked bad while it was being set up on the main thread, + // so if there is still an error, return false. + if (m_error) + return false; + + db.setAuthorizerPermissions(m_permissions); + + SQLiteDatabase& database = db.sqliteDatabase(); + + SQLiteStatement statement(database, m_statement); + int result = statement.prepare(); + + if (result != SQLITE_OK) { + LOG(StorageAPI, "Unable to verify correctness of statement %s - error %i (%s)", m_statement.ascii().data(), result, database.lastErrorMsg()); + if (result == SQLITE_INTERRUPT) + m_error = SQLError::create(SQLError::DATABASE_ERR, "could not prepare statement", result, "interrupted"); + else + m_error = SQLError::create(SQLError::SYNTAX_ERR, "could not prepare statement", result, database.lastErrorMsg()); + return false; + } + + // FIXME: If the statement uses the ?### syntax supported by sqlite, the bind parameter count is very likely off from the number of question marks. + // If this is the case, they might be trying to do something fishy or malicious + if (statement.bindParameterCount() != m_arguments.size()) { + LOG(StorageAPI, "Bind parameter count doesn't match number of question marks"); + m_error = SQLError::create(SQLError::SYNTAX_ERR, "number of '?'s in statement string does not match argument count"); + return false; + } + + for (unsigned i = 0; i < m_arguments.size(); ++i) { + result = statement.bindValue(i + 1, m_arguments[i]); + if (result == SQLITE_FULL) { + setFailureDueToQuota(); + return false; + } + + if (result != SQLITE_OK) { + LOG(StorageAPI, "Failed to bind value index %i to statement for query '%s'", i + 1, m_statement.ascii().data()); + m_error = SQLError::create(SQLError::DATABASE_ERR, "could not bind value", result, database.lastErrorMsg()); + return false; + } + } + + RefPtr<SQLResultSet> resultSet = SQLResultSet::create(); + + // Step so we can fetch the column names. + result = statement.step(); + switch (result) { + case SQLITE_ROW: { + int columnCount = statement.columnCount(); + auto& rows = resultSet->rows(); + + for (int i = 0; i < columnCount; i++) + rows.addColumn(statement.getColumnName(i)); + + do { + for (int i = 0; i < columnCount; i++) + rows.addResult(statement.getColumnValue(i)); + + result = statement.step(); + } while (result == SQLITE_ROW); + + if (result != SQLITE_DONE) { + m_error = SQLError::create(SQLError::DATABASE_ERR, "could not iterate results", result, database.lastErrorMsg()); + return false; + } + break; + } + case SQLITE_DONE: { + // Didn't find anything, or was an insert + if (db.lastActionWasInsert()) + resultSet->setInsertId(database.lastInsertRowID()); + break; + } + case SQLITE_FULL: + // Return the Quota error - the delegate will be asked for more space and this statement might be re-run + setFailureDueToQuota(); + return false; + case SQLITE_CONSTRAINT: + m_error = SQLError::create(SQLError::CONSTRAINT_ERR, "could not execute statement due to a constaint failure", result, database.lastErrorMsg()); + return false; + default: + m_error = SQLError::create(SQLError::DATABASE_ERR, "could not execute statement", result, database.lastErrorMsg()); + return false; + } + + // FIXME: If the spec allows triggers, and we want to be "accurate" in a different way, we'd use + // sqlite3_total_changes() here instead of sqlite3_changed, because that includes rows modified from within a trigger + // For now, this seems sufficient + resultSet->setRowsAffected(database.lastChanges()); + + m_resultSet = resultSet; + return true; } bool SQLStatement::performCallback(SQLTransaction* transaction) { ASSERT(transaction); - ASSERT(m_backend); bool callbackError = false; RefPtr<SQLStatementCallback> callback = m_statementCallbackWrapper.unwrap(); RefPtr<SQLStatementErrorCallback> errorCallback = m_statementErrorCallbackWrapper.unwrap(); - RefPtr<SQLError> error = m_backend->sqlError(); + RefPtr<SQLError> error = sqlError(); // Call the appropriate statement callback and track if it resulted in an error, // because then we need to jump to the transaction error callback. @@ -90,13 +215,40 @@ bool SQLStatement::performCallback(SQLTransaction* transaction) if (errorCallback) callbackError = errorCallback->handleEvent(transaction, error.get()); } else if (callback) { - RefPtr<SQLResultSet> resultSet = m_backend->sqlResultSet(); + RefPtr<SQLResultSet> resultSet = sqlResultSet(); callbackError = !callback->handleEvent(transaction, resultSet.get()); } return callbackError; } -} // namespace WebCore +void SQLStatement::setDatabaseDeletedError() +{ + ASSERT(!m_error && !m_resultSet); + m_error = SQLError::create(SQLError::UNKNOWN_ERR, "unable to execute statement, because the user deleted the database"); +} + +void SQLStatement::setVersionMismatchedError() +{ + ASSERT(!m_error && !m_resultSet); + m_error = SQLError::create(SQLError::VERSION_ERR, "current version of the database and `oldVersion` argument do not match"); +} -#endif // ENABLE(SQL_DATABASE) +void SQLStatement::setFailureDueToQuota() +{ + ASSERT(!m_error && !m_resultSet); + m_error = SQLError::create(SQLError::QUOTA_ERR, "there was not enough remaining storage space, or the storage quota was reached and the user declined to allow more space"); +} + +void SQLStatement::clearFailureDueToQuota() +{ + if (lastExecutionFailedDueToQuota()) + m_error = nullptr; +} + +bool SQLStatement::lastExecutionFailedDueToQuota() const +{ + return m_error && m_error->code() == SQLError::QUOTA_ERR; +} + +} // namespace WebCore diff --git a/Source/WebCore/Modules/webdatabase/SQLStatement.h b/Source/WebCore/Modules/webdatabase/SQLStatement.h index 4fdb2dab1..239ea933c 100644 --- a/Source/WebCore/Modules/webdatabase/SQLStatement.h +++ b/Source/WebCore/Modules/webdatabase/SQLStatement.h @@ -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. * @@ -20,19 +20,17 @@ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * LOSS OF USE, DATA, OR 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. */ -#ifndef SQLStatement_h -#define SQLStatement_h -#if ENABLE(SQL_DATABASE) +#pragma once -#include "AbstractSQLStatement.h" #include "SQLCallbackWrapper.h" -#include "SQLResultSet.h" +#include "SQLStatementCallback.h" +#include "SQLStatementErrorCallback.h" #include "SQLValue.h" #include <wtf/Forward.h> #include <wtf/Vector.h> @@ -40,39 +38,42 @@ namespace WebCore { -class AbstractSQLStatementBackend; class Database; class SQLError; -class SQLStatementCallback; -class SQLStatementErrorCallback; -class SQLTransaction; +class SQLResultSet; +class SQLTransactionBackend; -class SQLStatement : public AbstractSQLStatement { +class SQLStatement { public: - static PassOwnPtr<SQLStatement> create(Database*, - PassRefPtr<SQLStatementCallback>, PassRefPtr<SQLStatementErrorCallback>); + SQLStatement(Database&, const String&, Vector<SQLValue>&&, RefPtr<SQLStatementCallback>&&, RefPtr<SQLStatementErrorCallback>&&, int permissions); + ~SQLStatement(); + bool execute(Database&); + bool lastExecutionFailedDueToQuota() const; + + bool hasStatementCallback() const { return m_statementCallbackWrapper.hasCallback(); } + bool hasStatementErrorCallback() const { return m_statementErrorCallbackWrapper.hasCallback(); } bool performCallback(SQLTransaction*); - virtual void setBackend(AbstractSQLStatementBackend*); + void setDatabaseDeletedError(); + void setVersionMismatchedError(); - virtual bool hasCallback(); - virtual bool hasErrorCallback(); + SQLError* sqlError() const; + SQLResultSet* sqlResultSet() const; private: - SQLStatement(Database*, PassRefPtr<SQLStatementCallback>, PassRefPtr<SQLStatementErrorCallback>); - - // The AbstractSQLStatementBackend owns the SQLStatement. Hence, the backend is - // guaranteed to be outlive the SQLStatement, and it is safe for us to refer - // to the backend using a raw pointer here. - AbstractSQLStatementBackend* m_backend; + void setFailureDueToQuota(); + void clearFailureDueToQuota(); + String m_statement; + Vector<SQLValue> m_arguments; SQLCallbackWrapper<SQLStatementCallback> m_statementCallbackWrapper; SQLCallbackWrapper<SQLStatementErrorCallback> m_statementErrorCallbackWrapper; -}; -} // namespace WebCore + RefPtr<SQLError> m_error; + RefPtr<SQLResultSet> m_resultSet; -#endif // ENABLE(SQL_DATABASE) + int m_permissions; +}; -#endif // SQLStatement_h +} // namespace WebCore diff --git a/Source/WebCore/Modules/webdatabase/SQLStatementBackend.cpp b/Source/WebCore/Modules/webdatabase/SQLStatementBackend.cpp deleted file mode 100644 index 3873e2237..000000000 --- a/Source/WebCore/Modules/webdatabase/SQLStatementBackend.cpp +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright (C) 2007, 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 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 - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR 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. - */ -#include "config.h" -#include "SQLStatementBackend.h" - -#if ENABLE(SQL_DATABASE) - -#include "AbstractSQLStatement.h" -#include "DatabaseBackend.h" -#include "Logging.h" -#include "SQLError.h" -#include "SQLValue.h" -#include "SQLiteDatabase.h" -#include "SQLiteStatement.h" -#include <wtf/text/CString.h> - - -// The Life-Cycle of a SQLStatement i.e. Who's keeping the SQLStatement alive? -// ========================================================================== -// The RefPtr chain goes something like this: -// -// At birth (in SQLTransactionBackend::executeSQL()): -// ================================================= -// SQLTransactionBackend // Deque<RefPtr<SQLStatementBackend>> m_statementQueue points to ... -// --> SQLStatementBackend // OwnPtr<SQLStatement> m_frontend points to ... -// --> SQLStatement -// -// After grabbing the statement for execution (in SQLTransactionBackend::getNextStatement()): -// ========================================================================================= -// SQLTransactionBackend // RefPtr<SQLStatementBackend> m_currentStatementBackend points to ... -// --> SQLStatementBackend // OwnPtr<SQLStatement> m_frontend points to ... -// --> SQLStatement -// -// Then we execute the statement in SQLTransactionBackend::runCurrentStatementAndGetNextState(). -// And we callback to the script in SQLTransaction::deliverStatementCallback() if -// necessary. -// - Inside SQLTransaction::deliverStatementCallback(), we operate on a raw SQLStatement*. -// This pointer is valid because it is owned by SQLTransactionBackend's -// SQLTransactionBackend::m_currentStatementBackend. -// -// After we're done executing the statement (in SQLTransactionBackend::getNextStatement()): -// ======================================================================================= -// When we're done executing, we'll grab the next statement. But before we -// do that, getNextStatement() nullify SQLTransactionBackend::m_currentStatementBackend. -// This will trigger the deletion of the SQLStatementBackend and SQLStatement. -// -// Note: unlike with SQLTransaction, there is no JS representation of SQLStatement. -// Hence, there is no GC dependency at play here. - -namespace WebCore { - -PassRefPtr<SQLStatementBackend> SQLStatementBackend::create(PassOwnPtr<AbstractSQLStatement> frontend, - const String& statement, const Vector<SQLValue>& arguments, int permissions) -{ - return adoptRef(new SQLStatementBackend(frontend, statement, arguments, permissions)); -} - -SQLStatementBackend::SQLStatementBackend(PassOwnPtr<AbstractSQLStatement> frontend, - const String& statement, const Vector<SQLValue>& arguments, int permissions) - : m_frontend(frontend) - , m_statement(statement.isolatedCopy()) - , m_arguments(arguments) - , m_hasCallback(m_frontend->hasCallback()) - , m_hasErrorCallback(m_frontend->hasErrorCallback()) - , m_permissions(permissions) -{ - m_frontend->setBackend(this); -} - -AbstractSQLStatement* SQLStatementBackend::frontend() -{ - return m_frontend.get(); -} - -PassRefPtr<SQLError> SQLStatementBackend::sqlError() const -{ - return m_error; -} - -PassRefPtr<SQLResultSet> SQLStatementBackend::sqlResultSet() const -{ - return m_resultSet; -} - -bool SQLStatementBackend::execute(DatabaseBackend* db) -{ - ASSERT(!m_resultSet); - - // If we're re-running this statement after a quota violation, we need to clear that error now - clearFailureDueToQuota(); - - // This transaction might have been marked bad while it was being set up on the main thread, - // so if there is still an error, return false. - if (m_error) - return false; - - db->setAuthorizerPermissions(m_permissions); - - SQLiteDatabase* database = &db->sqliteDatabase(); - - SQLiteStatement statement(*database, m_statement); - int result = statement.prepare(); - - if (result != SQLResultOk) { - LOG(StorageAPI, "Unable to verify correctness of statement %s - error %i (%s)", m_statement.ascii().data(), result, database->lastErrorMsg()); - if (result == SQLResultInterrupt) - m_error = SQLError::create(SQLError::DATABASE_ERR, "could not prepare statement", result, "interrupted"); - else - m_error = SQLError::create(SQLError::SYNTAX_ERR, "could not prepare statement", result, database->lastErrorMsg()); - return false; - } - - // FIXME: If the statement uses the ?### syntax supported by sqlite, the bind parameter count is very likely off from the number of question marks. - // If this is the case, they might be trying to do something fishy or malicious - if (statement.bindParameterCount() != m_arguments.size()) { - LOG(StorageAPI, "Bind parameter count doesn't match number of question marks"); - m_error = SQLError::create(db->isInterrupted() ? SQLError::DATABASE_ERR : SQLError::SYNTAX_ERR, "number of '?'s in statement string does not match argument count"); - return false; - } - - for (unsigned i = 0; i < m_arguments.size(); ++i) { - result = statement.bindValue(i + 1, m_arguments[i]); - if (result == SQLResultFull) { - setFailureDueToQuota(); - return false; - } - - if (result != SQLResultOk) { - LOG(StorageAPI, "Failed to bind value index %i to statement for query '%s'", i + 1, m_statement.ascii().data()); - m_error = SQLError::create(SQLError::DATABASE_ERR, "could not bind value", result, database->lastErrorMsg()); - return false; - } - } - - RefPtr<SQLResultSet> resultSet = SQLResultSet::create(); - - // Step so we can fetch the column names. - result = statement.step(); - if (result == SQLResultRow) { - int columnCount = statement.columnCount(); - SQLResultSetRowList* rows = resultSet->rows(); - - for (int i = 0; i < columnCount; i++) - rows->addColumn(statement.getColumnName(i)); - - do { - for (int i = 0; i < columnCount; i++) - rows->addResult(statement.getColumnValue(i)); - - result = statement.step(); - } while (result == SQLResultRow); - - if (result != SQLResultDone) { - m_error = SQLError::create(SQLError::DATABASE_ERR, "could not iterate results", result, database->lastErrorMsg()); - return false; - } - } else if (result == SQLResultDone) { - // Didn't find anything, or was an insert - if (db->lastActionWasInsert()) - resultSet->setInsertId(database->lastInsertRowID()); - } else if (result == SQLResultFull) { - // Return the Quota error - the delegate will be asked for more space and this statement might be re-run - setFailureDueToQuota(); - return false; - } else if (result == SQLResultConstraint) { - m_error = SQLError::create(SQLError::CONSTRAINT_ERR, "could not execute statement due to a constaint failure", result, database->lastErrorMsg()); - return false; - } else { - m_error = SQLError::create(SQLError::DATABASE_ERR, "could not execute statement", result, database->lastErrorMsg()); - return false; - } - - // FIXME: If the spec allows triggers, and we want to be "accurate" in a different way, we'd use - // sqlite3_total_changes() here instead of sqlite3_changed, because that includes rows modified from within a trigger - // For now, this seems sufficient - resultSet->setRowsAffected(database->lastChanges()); - - m_resultSet = resultSet; - return true; -} - -void SQLStatementBackend::setDatabaseDeletedError() -{ - ASSERT(!m_error && !m_resultSet); - m_error = SQLError::create(SQLError::UNKNOWN_ERR, "unable to execute statement, because the user deleted the database"); -} - -void SQLStatementBackend::setVersionMismatchedError() -{ - ASSERT(!m_error && !m_resultSet); - m_error = SQLError::create(SQLError::VERSION_ERR, "current version of the database and `oldVersion` argument do not match"); -} - -void SQLStatementBackend::setFailureDueToQuota() -{ - ASSERT(!m_error && !m_resultSet); - m_error = SQLError::create(SQLError::QUOTA_ERR, "there was not enough remaining storage space, or the storage quota was reached and the user declined to allow more space"); -} - -void SQLStatementBackend::clearFailureDueToQuota() -{ - if (lastExecutionFailedDueToQuota()) - m_error = 0; -} - -bool SQLStatementBackend::lastExecutionFailedDueToQuota() const -{ - return m_error && m_error->code() == SQLError::QUOTA_ERR; -} - -} // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) diff --git a/Source/WebCore/Modules/webdatabase/SQLStatementBackend.h b/Source/WebCore/Modules/webdatabase/SQLStatementBackend.h deleted file mode 100644 index a2d2690d9..000000000 --- a/Source/WebCore/Modules/webdatabase/SQLStatementBackend.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2007, 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 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 - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR 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. - */ -#ifndef SQLStatementBackend_h -#define SQLStatementBackend_h - -#if ENABLE(SQL_DATABASE) - -#include "AbstractSQLStatementBackend.h" -#include "SQLValue.h" -#include <wtf/Forward.h> -#include <wtf/PassOwnPtr.h> -#include <wtf/Vector.h> -#include <wtf/text/WTFString.h> - -namespace WebCore { - -class AbstractSQLStatement; -class DatabaseBackend; -class SQLError; -class SQLTransactionBackend; - -class SQLStatementBackend : public AbstractSQLStatementBackend { -public: - static PassRefPtr<SQLStatementBackend> create(PassOwnPtr<AbstractSQLStatement>, - const String& sqlStatement, const Vector<SQLValue>& arguments, int permissions); - - bool execute(DatabaseBackend*); - bool lastExecutionFailedDueToQuota() const; - - bool hasStatementCallback() const { return m_hasCallback; } - bool hasStatementErrorCallback() const { return m_hasErrorCallback; } - - void setDatabaseDeletedError(); - void setVersionMismatchedError(); - - AbstractSQLStatement* frontend(); - virtual PassRefPtr<SQLError> sqlError() const; - virtual PassRefPtr<SQLResultSet> sqlResultSet() const; - -private: - SQLStatementBackend(PassOwnPtr<AbstractSQLStatement>, const String& statement, - const Vector<SQLValue>& arguments, int permissions); - - void setFailureDueToQuota(); - void clearFailureDueToQuota(); - - OwnPtr<AbstractSQLStatement> m_frontend; - String m_statement; - Vector<SQLValue> m_arguments; - bool m_hasCallback; - bool m_hasErrorCallback; - - RefPtr<SQLError> m_error; - RefPtr<SQLResultSet> m_resultSet; - - int m_permissions; -}; - -} // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) - -#endif // SQLStatementBackend_h diff --git a/Source/WebCore/Modules/webdatabase/SQLStatementCallback.h b/Source/WebCore/Modules/webdatabase/SQLStatementCallback.h index 83201d2b8..6da7bc4f3 100644 --- a/Source/WebCore/Modules/webdatabase/SQLStatementCallback.h +++ b/Source/WebCore/Modules/webdatabase/SQLStatementCallback.h @@ -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. * @@ -25,10 +25,8 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SQLStatementCallback_h -#define SQLStatementCallback_h -#if ENABLE(SQL_DATABASE) +#pragma once #include <wtf/ThreadSafeRefCounted.h> @@ -43,8 +41,4 @@ public: virtual bool handleEvent(SQLTransaction*, SQLResultSet*) = 0; }; -} - -#endif - -#endif // SQLStatementErrorCallback_h +} // namespace WebCore diff --git a/Source/WebCore/Modules/webdatabase/SQLStatementCallback.idl b/Source/WebCore/Modules/webdatabase/SQLStatementCallback.idl index 58feec93b..75f81f59c 100644 --- a/Source/WebCore/Modules/webdatabase/SQLStatementCallback.idl +++ b/Source/WebCore/Modules/webdatabase/SQLStatementCallback.idl @@ -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. * @@ -26,8 +26,4 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -[ - Conditional=SQL_DATABASE, -] callback interface SQLStatementCallback { - boolean handleEvent(SQLTransaction transaction, SQLResultSet resultSet); -}; +callback SQLStatementCallback = void (SQLTransaction transaction, SQLResultSet resultSet); diff --git a/Source/WebCore/Modules/webdatabase/SQLStatementErrorCallback.h b/Source/WebCore/Modules/webdatabase/SQLStatementErrorCallback.h index c7c9f5450..ca5947ae9 100644 --- a/Source/WebCore/Modules/webdatabase/SQLStatementErrorCallback.h +++ b/Source/WebCore/Modules/webdatabase/SQLStatementErrorCallback.h @@ -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. * @@ -26,10 +26,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SQLStatementErrorCallback_h -#define SQLStatementErrorCallback_h - -#if ENABLE(SQL_DATABASE) +#pragma once #include <wtf/ThreadSafeRefCounted.h> @@ -44,8 +41,4 @@ public: virtual bool handleEvent(SQLTransaction*, SQLError*) = 0; }; -} - -#endif - -#endif // SQLStatementErrorCallback_h +} // namespace WebCore diff --git a/Source/WebCore/Modules/webdatabase/SQLStatementErrorCallback.idl b/Source/WebCore/Modules/webdatabase/SQLStatementErrorCallback.idl index 5e3df6652..c68482315 100644 --- a/Source/WebCore/Modules/webdatabase/SQLStatementErrorCallback.idl +++ b/Source/WebCore/Modules/webdatabase/SQLStatementErrorCallback.idl @@ -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. * @@ -27,7 +27,5 @@ */ [ - Conditional=SQL_DATABASE, -] callback interface SQLStatementErrorCallback { - [Custom] boolean handleEvent(SQLTransaction transaction, SQLError error); -}; + Custom +] callback SQLStatementErrorCallback = boolean (SQLTransaction transaction, SQLError error); diff --git a/Source/WebCore/Modules/webdatabase/SQLStatementSync.cpp b/Source/WebCore/Modules/webdatabase/SQLStatementSync.cpp deleted file mode 100644 index 30ad956e4..000000000 --- a/Source/WebCore/Modules/webdatabase/SQLStatementSync.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2007 Apple Inc. All rights reserved. - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 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 - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR 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. - */ - -#include "config.h" -#include "SQLStatementSync.h" - -#if ENABLE(SQL_DATABASE) - -#include "DatabaseSync.h" -#include "SQLException.h" -#include "SQLResultSet.h" -#include "SQLValue.h" -#include "SQLiteDatabase.h" -#include "SQLiteStatement.h" -#include <wtf/PassRefPtr.h> -#include <wtf/RefPtr.h> - -namespace WebCore { - -SQLStatementSync::SQLStatementSync(const String& statement, const Vector<SQLValue>& arguments, int permissions) - : m_statement(statement) - , m_arguments(arguments) - , m_permissions(permissions) -{ - ASSERT(!m_statement.isEmpty()); -} - -PassRefPtr<SQLResultSet> SQLStatementSync::execute(DatabaseSync* db, ExceptionCode& ec) -{ - db->setAuthorizerPermissions(m_permissions); - - SQLiteDatabase* database = &db->sqliteDatabase(); - - SQLiteStatement statement(*database, m_statement); - int result = statement.prepare(); - if (result != SQLResultOk) { - ec = (result == SQLResultInterrupt ? SQLException::DATABASE_ERR : SQLException::SYNTAX_ERR); - db->setLastErrorMessage("could not prepare statement", result, database->lastErrorMsg()); - return 0; - } - - if (statement.bindParameterCount() != m_arguments.size()) { - ec = (db->isInterrupted()? SQLException::DATABASE_ERR : SQLException::SYNTAX_ERR); - db->setLastErrorMessage("number of '?'s in statement string does not match argument count"); - return 0; - } - - for (unsigned i = 0; i < m_arguments.size(); ++i) { - result = statement.bindValue(i + 1, m_arguments[i]); - if (result == SQLResultFull) { - ec = SQLException::QUOTA_ERR; - db->setLastErrorMessage("there was not enough remaining storage space"); - return 0; - } - - if (result != SQLResultOk) { - ec = SQLException::DATABASE_ERR; - db->setLastErrorMessage("could not bind value", result, database->lastErrorMsg()); - return 0; - } - } - - RefPtr<SQLResultSet> resultSet = SQLResultSet::create(); - - // Step so we can fetch the column names. - result = statement.step(); - if (result == SQLResultRow) { - int columnCount = statement.columnCount(); - SQLResultSetRowList* rows = resultSet->rows(); - - for (int i = 0; i < columnCount; i++) - rows->addColumn(statement.getColumnName(i)); - - do { - for (int i = 0; i < columnCount; i++) - rows->addResult(statement.getColumnValue(i)); - - result = statement.step(); - } while (result == SQLResultRow); - - if (result != SQLResultDone) { - ec = SQLException::DATABASE_ERR; - db->setLastErrorMessage("could not iterate results", result, database->lastErrorMsg()); - return 0; - } - } else if (result == SQLResultDone) { - // Didn't find anything, or was an insert. - if (db->lastActionWasInsert()) - resultSet->setInsertId(database->lastInsertRowID()); - } else if (result == SQLResultFull) { - // Quota error, the delegate will be asked for more space and this statement might be re-run. - ec = SQLException::QUOTA_ERR; - db->setLastErrorMessage("there was not enough remaining storage space"); - return 0; - } else if (result == SQLResultConstraint) { - ec = SQLException::CONSTRAINT_ERR; - db->setLastErrorMessage("statement failed due to a constraint failure"); - return 0; - } else { - ec = SQLException::DATABASE_ERR; - db->setLastErrorMessage("could not execute statement", result, database->lastErrorMsg()); - return 0; - } - - resultSet->setRowsAffected(database->lastChanges()); - return resultSet.release(); -} - -} // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) diff --git a/Source/WebCore/Modules/webdatabase/SQLStatementSync.h b/Source/WebCore/Modules/webdatabase/SQLStatementSync.h deleted file mode 100644 index 3278b9587..000000000 --- a/Source/WebCore/Modules/webdatabase/SQLStatementSync.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * 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. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR 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. - */ - -#ifndef SQLStatementSync_h -#define SQLStatementSync_h - -#if ENABLE(SQL_DATABASE) - -#include "DatabaseBasicTypes.h" -#include "SQLValue.h" -#include <wtf/Forward.h> -#include <wtf/Vector.h> -#include <wtf/text/WTFString.h> - -namespace WebCore { - -class DatabaseSync; -class SQLResultSet; - -class SQLStatementSync { -public: - SQLStatementSync(const String& statement, const Vector<SQLValue>& arguments, int permissions); - - PassRefPtr<SQLResultSet> execute(DatabaseSync*, ExceptionCode&); - -private: - String m_statement; - Vector<SQLValue> m_arguments; - int m_permissions; -}; - -} // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) - -#endif // SQLStatementSync_h diff --git a/Source/WebCore/Modules/webdatabase/SQLTransaction.cpp b/Source/WebCore/Modules/webdatabase/SQLTransaction.cpp index 2b4f7bb22..76cdd7c7b 100644 --- a/Source/WebCore/Modules/webdatabase/SQLTransaction.cpp +++ b/Source/WebCore/Modules/webdatabase/SQLTransaction.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,65 +29,110 @@ #include "config.h" #include "SQLTransaction.h" -#if ENABLE(SQL_DATABASE) - -#include "AbstractSQLTransactionBackend.h" #include "Database.h" #include "DatabaseAuthorizer.h" #include "DatabaseContext.h" +#include "DatabaseThread.h" +#include "DatabaseTracker.h" #include "ExceptionCode.h" #include "Logging.h" +#include "OriginLock.h" #include "SQLError.h" +#include "SQLStatement.h" #include "SQLStatementCallback.h" #include "SQLStatementErrorCallback.h" +#include "SQLTransactionBackend.h" #include "SQLTransactionCallback.h" -#include "SQLTransactionClient.h" // FIXME: Should be used in the backend only. +#include "SQLTransactionCoordinator.h" #include "SQLTransactionErrorCallback.h" +#include "SQLiteTransaction.h" #include "VoidCallback.h" #include <wtf/StdLibExtras.h> #include <wtf/Vector.h> namespace WebCore { -PassRefPtr<SQLTransaction> SQLTransaction::create(Database* db, PassRefPtr<SQLTransactionCallback> callback, - PassRefPtr<VoidCallback> successCallback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, - bool readOnly) +Ref<SQLTransaction> SQLTransaction::create(Ref<Database>&& database, RefPtr<SQLTransactionCallback>&& callback, RefPtr<VoidCallback>&& successCallback, RefPtr<SQLTransactionErrorCallback>&& errorCallback, RefPtr<SQLTransactionWrapper>&& wrapper, bool readOnly) { - return adoptRef(new SQLTransaction(db, callback, successCallback, errorCallback, readOnly)); + return adoptRef(*new SQLTransaction(WTFMove(database), WTFMove(callback), WTFMove(successCallback), WTFMove(errorCallback), WTFMove(wrapper), readOnly)); } -SQLTransaction::SQLTransaction(Database* db, PassRefPtr<SQLTransactionCallback> callback, - PassRefPtr<VoidCallback> successCallback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, - bool readOnly) - : m_database(db) - , m_callbackWrapper(callback, db->scriptExecutionContext()) - , m_successCallbackWrapper(successCallback, db->scriptExecutionContext()) - , m_errorCallbackWrapper(errorCallback, db->scriptExecutionContext()) - , m_executeSqlAllowed(false) +SQLTransaction::SQLTransaction(Ref<Database>&& database, RefPtr<SQLTransactionCallback>&& callback, RefPtr<VoidCallback>&& successCallback, RefPtr<SQLTransactionErrorCallback>&& errorCallback, RefPtr<SQLTransactionWrapper>&& wrapper, bool readOnly) + : m_database(WTFMove(database)) + , m_callbackWrapper(WTFMove(callback), &m_database->scriptExecutionContext()) + , m_successCallbackWrapper(WTFMove(successCallback), &m_database->scriptExecutionContext()) + , m_errorCallbackWrapper(WTFMove(errorCallback), &m_database->scriptExecutionContext()) + , m_wrapper(WTFMove(wrapper)) + , m_nextStep(&SQLTransaction::acquireLock) , m_readOnly(readOnly) + , m_backend(*this) +{ +} + +SQLTransaction::~SQLTransaction() +{ +} + +ExceptionOr<void> SQLTransaction::executeSql(const String& sqlStatement, std::optional<Vector<SQLValue>>&& arguments, RefPtr<SQLStatementCallback>&& callback, RefPtr<SQLStatementErrorCallback>&& callbackError) +{ + if (!m_executeSqlAllowed || !m_database->opened()) + return Exception { INVALID_STATE_ERR }; + + int permissions = DatabaseAuthorizer::ReadWriteMask; + if (!m_database->databaseContext().allowDatabaseAccess()) + permissions |= DatabaseAuthorizer::NoAccessMask; + else if (m_readOnly) + permissions |= DatabaseAuthorizer::ReadOnlyMask; + + auto statement = std::make_unique<SQLStatement>(m_database, sqlStatement, arguments.value_or(Vector<SQLValue> { }), WTFMove(callback), WTFMove(callbackError), permissions); + + if (m_database->deleted()) + statement->setDatabaseDeletedError(); + + enqueueStatement(WTFMove(statement)); + + return { }; +} + +void SQLTransaction::lockAcquired() { - ASSERT(m_database); + m_lockAcquired = true; + + m_backend.m_requestedState = SQLTransactionState::OpenTransactionAndPreflight; + m_database->scheduleTransactionStep(*this); } -bool SQLTransaction::hasCallback() const +void SQLTransaction::performNextStep() { - return m_callbackWrapper.hasCallback(); + m_backend.computeNextStateAndCleanupIfNeeded(); + m_backend.runStateMachine(); } -bool SQLTransaction::hasSuccessCallback() const +void SQLTransaction::performPendingCallback() { - return m_successCallbackWrapper.hasCallback(); + LOG(StorageAPI, "Callback %s\n", debugStepName(m_nextStep)); + + ASSERT(m_nextStep == &SQLTransaction::deliverTransactionCallback + || m_nextStep == &SQLTransaction::deliverTransactionErrorCallback + || m_nextStep == &SQLTransaction::deliverStatementCallback + || m_nextStep == &SQLTransaction::deliverQuotaIncreaseCallback + || m_nextStep == &SQLTransaction::deliverSuccessCallback); + + checkAndHandleClosedDatabase(); + + if (m_nextStep) + (this->*m_nextStep)(); } -bool SQLTransaction::hasErrorCallback() const +void SQLTransaction::notifyDatabaseThreadIsShuttingDown() { - return m_errorCallbackWrapper.hasCallback(); + m_backend.notifyDatabaseThreadIsShuttingDown(); } -void SQLTransaction::setBackend(AbstractSQLTransactionBackend* backend) +void SQLTransaction::enqueueStatement(std::unique_ptr<SQLStatement> statement) { - ASSERT(!m_backend); - m_backend = backend; + LockHolder locker(m_statementMutex); + m_statementQueue.append(WTFMove(statement)); } SQLTransaction::StateFunction SQLTransaction::stateFunctionFor(SQLTransactionState state) @@ -97,10 +142,10 @@ SQLTransaction::StateFunction SQLTransaction::stateFunctionFor(SQLTransactionSta &SQLTransaction::unreachableState, // 1. idle &SQLTransaction::unreachableState, // 2. acquireLock &SQLTransaction::unreachableState, // 3. openTransactionAndPreflight - &SQLTransaction::sendToBackendState, // 4. runStatements + &SQLTransaction::unreachableState, // 4. runStatements &SQLTransaction::unreachableState, // 5. postflightAndCommit - &SQLTransaction::sendToBackendState, // 6. cleanupAndTerminate - &SQLTransaction::sendToBackendState, // 7. cleanupAfterTransactionErrorCallback + &SQLTransaction::unreachableState, // 6. cleanupAndTerminate + &SQLTransaction::unreachableState, // 7. cleanupAfterTransactionErrorCallback &SQLTransaction::deliverTransactionCallback, // 8. &SQLTransaction::deliverTransactionErrorCallback, // 9. &SQLTransaction::deliverStatementCallback, // 10. @@ -124,18 +169,201 @@ void SQLTransaction::requestTransitToState(SQLTransactionState nextState) m_database->scheduleTransactionCallback(this); } -SQLTransactionState SQLTransaction::nextStateForTransactionError() +void SQLTransaction::checkAndHandleClosedDatabase() { - ASSERT(m_transactionError); - if (m_errorCallbackWrapper.hasCallback()) - return SQLTransactionState::DeliverTransactionErrorCallback; + if (m_database->opened()) + return; + + // If the database was stopped, don't do anything and cancel queued work + LOG(StorageAPI, "Database was stopped or interrupted - cancelling work for this transaction"); + + LockHolder locker(m_statementMutex); + m_statementQueue.clear(); + m_nextStep = nullptr; + + // Release the unneeded callbacks, to break reference cycles. + m_callbackWrapper.clear(); + m_successCallbackWrapper.clear(); + m_errorCallbackWrapper.clear(); + + // The next steps should be executed only if we're on the DB thread. + if (currentThread() != m_database->databaseThread().getThreadID()) + return; + + // The current SQLite transaction should be stopped, as well + if (m_sqliteTransaction) { + m_sqliteTransaction->stop(); + m_sqliteTransaction = nullptr; + } + + if (m_lockAcquired) + m_database->transactionCoordinator()->releaseLock(*this); +} + +void SQLTransaction::scheduleCallback(void (SQLTransaction::*step)()) +{ + m_nextStep = step; + + LOG(StorageAPI, "Scheduling %s for transaction %p\n", debugStepName(step), this); + m_database->scheduleTransactionCallback(this); +} + +void SQLTransaction::acquireLock() +{ + m_database->transactionCoordinator()->acquireLock(*this); +} + +void SQLTransaction::openTransactionAndPreflight() +{ + ASSERT(!m_database->sqliteDatabase().transactionInProgress()); + ASSERT(m_lockAcquired); + + LOG(StorageAPI, "Opening and preflighting transaction %p", this); + + // If the database was deleted, jump to the error callback + if (m_database->deleted()) { + m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unable to open a transaction, because the user deleted the database"); + + handleTransactionError(); + return; + } + + // Set the maximum usage for this transaction if this transactions is not read-only + if (!m_readOnly) { + acquireOriginLock(); + m_database->sqliteDatabase().setMaximumSize(m_database->maximumSize()); + } + + ASSERT(!m_sqliteTransaction); + m_sqliteTransaction = std::make_unique<SQLiteTransaction>(m_database->sqliteDatabase(), m_readOnly); + + m_database->resetDeletes(); + m_database->disableAuthorizer(); + m_sqliteTransaction->begin(); + m_database->enableAuthorizer(); + + // Spec 4.3.2.1+2: Open a transaction to the database, jumping to the error callback if that fails + if (!m_sqliteTransaction->inProgress()) { + ASSERT(!m_database->sqliteDatabase().transactionInProgress()); + m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "unable to begin transaction", m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg()); + m_sqliteTransaction = nullptr; + + handleTransactionError(); + return; + } + + // Note: We intentionally retrieve the actual version even with an empty expected version. + // In multi-process browsers, we take this opportinutiy to update the cached value for + // the actual version. In single-process browsers, this is just a map lookup. + String actualVersion; + if (!m_database->getActualVersionForTransaction(actualVersion)) { + m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "unable to read version", m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg()); + m_database->disableAuthorizer(); + m_sqliteTransaction = nullptr; + m_database->enableAuthorizer(); + + handleTransactionError(); + return; + } + + m_hasVersionMismatch = !m_database->expectedVersion().isEmpty() && (m_database->expectedVersion() != actualVersion); + + // Spec 4.3.2.3: Perform preflight steps, jumping to the error callback if they fail + if (m_wrapper && !m_wrapper->performPreflight(*this)) { + m_database->disableAuthorizer(); + m_sqliteTransaction = nullptr; + m_database->enableAuthorizer(); + m_transactionError = m_wrapper->sqlError(); + if (!m_transactionError) + m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unknown error occurred during transaction preflight"); + + handleTransactionError(); + return; + } + + // Spec 4.3.2.4: Invoke the transaction callback with the new SQLTransaction object + if (m_callbackWrapper.hasCallback()) { + scheduleCallback(&SQLTransaction::deliverTransactionCallback); + return; + } + + // If we have no callback to make, skip pass to the state after: + runStatements(); +} + +void SQLTransaction::runStatements() +{ + ASSERT(m_lockAcquired); + + // If there is a series of statements queued up that are all successful and have no associated + // SQLStatementCallback objects, then we can burn through the queue + do { + if (m_shouldRetryCurrentStatement && !m_sqliteTransaction->wasRolledBackBySqlite()) { + m_shouldRetryCurrentStatement = false; + // FIXME - Another place that needs fixing up after <rdar://problem/5628468> is addressed. + // See ::openTransactionAndPreflight() for discussion + + // Reset the maximum size here, as it was increased to allow us to retry this statement. + // m_shouldRetryCurrentStatement is set to true only when a statement exceeds + // the quota, which can happen only in a read-write transaction. Therefore, there + // is no need to check here if the transaction is read-write. + m_database->sqliteDatabase().setMaximumSize(m_database->maximumSize()); + } else { + // If the current statement has already been run, failed due to quota constraints, and we're not retrying it, + // that means it ended in an error. Handle it now + if (m_currentStatement && m_currentStatement->lastExecutionFailedDueToQuota()) { + handleCurrentStatementError(); + break; + } + + // Otherwise, advance to the next statement + getNextStatement(); + } + } while (runCurrentStatement()); + + // If runCurrentStatement() returned false, that means either there was no current statement to run, + // or the current statement requires a callback to complete. In the later case, it also scheduled + // the callback or performed any other additional work so we can return. + if (!m_currentStatement) + postflightAndCommit(); +} + +void SQLTransaction::cleanupAndTerminate() +{ + ASSERT(m_lockAcquired); + + // Spec 4.3.2.9: End transaction steps. There is no next step. + LOG(StorageAPI, "Transaction %p is complete\n", this); + ASSERT(!m_database->sqliteDatabase().transactionInProgress()); + + // Phase 5 cleanup. See comment on the SQLTransaction life-cycle above. + m_backend.doCleanup(); + m_database->inProgressTransactionCompleted(); +} - // No error callback, so fast-forward to: - // Transaction Step 11 - Rollback the transaction. - return SQLTransactionState::CleanupAfterTransactionErrorCallback; +void SQLTransaction::cleanupAfterTransactionErrorCallback() +{ + ASSERT(m_lockAcquired); + + LOG(StorageAPI, "Transaction %p is complete with an error\n", this); + m_database->disableAuthorizer(); + if (m_sqliteTransaction) { + // Spec 4.3.2.10: Rollback the transaction. + m_sqliteTransaction->rollback(); + + ASSERT(!m_database->sqliteDatabase().transactionInProgress()); + m_sqliteTransaction = nullptr; + } + m_database->enableAuthorizer(); + + releaseOriginLockIfNeeded(); + + ASSERT(!m_database->sqliteDatabase().transactionInProgress()); + + cleanupAndTerminate(); } -SQLTransactionState SQLTransaction::deliverTransactionCallback() +void SQLTransaction::deliverTransactionCallback() { bool shouldDeliverErrorCallback = false; @@ -148,71 +376,66 @@ SQLTransactionState SQLTransaction::deliverTransactionCallback() } // Spec 4.3.2 5: If the transaction callback was null or raised an exception, jump to the error callback - SQLTransactionState nextState = SQLTransactionState::RunStatements; if (shouldDeliverErrorCallback) { m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "the SQLTransactionCallback was null or threw an exception"); - nextState = SQLTransactionState::DeliverTransactionErrorCallback; + return deliverTransactionErrorCallback(); } - return nextState; + + m_backend.requestTransitToState(SQLTransactionState::RunStatements); } -SQLTransactionState SQLTransaction::deliverTransactionErrorCallback() +void SQLTransaction::deliverTransactionErrorCallback() { + ASSERT(m_transactionError); + // Spec 4.3.2.10: If exists, invoke error callback with the last // error to have occurred in this transaction. RefPtr<SQLTransactionErrorCallback> errorCallback = m_errorCallbackWrapper.unwrap(); - if (errorCallback) { - // If we get here with an empty m_transactionError, then the backend - // must be waiting in the idle state waiting for this state to finish. - // Hence, it's thread safe to fetch the backend transactionError without - // a lock. - if (!m_transactionError) - m_transactionError = m_backend->transactionError(); - - ASSERT(m_transactionError); + if (errorCallback) errorCallback->handleEvent(m_transactionError.get()); - m_transactionError = 0; - } - clearCallbackWrappers(); // Spec 4.3.2.10: Rollback the transaction. - return SQLTransactionState::CleanupAfterTransactionErrorCallback; + m_backend.requestTransitToState(SQLTransactionState::CleanupAfterTransactionErrorCallback); } -SQLTransactionState SQLTransaction::deliverStatementCallback() +void SQLTransaction::deliverStatementCallback() { + ASSERT(m_currentStatement); + // Spec 4.3.2.6.6 and 4.3.2.6.3: If the statement callback went wrong, jump to the transaction error callback // Otherwise, continue to loop through the statement queue m_executeSqlAllowed = true; - - AbstractSQLStatement* currentAbstractStatement = m_backend->currentStatement(); - SQLStatement* currentStatement = static_cast<SQLStatement*>(currentAbstractStatement); - ASSERT(currentStatement); - - bool result = currentStatement->performCallback(this); - + bool result = m_currentStatement->performCallback(this); m_executeSqlAllowed = false; if (result) { m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "the statement callback raised an exception or statement error callback did not return false"); - return nextStateForTransactionError(); + + if (m_errorCallbackWrapper.hasCallback()) + return deliverTransactionErrorCallback(); + + // No error callback, so fast-forward to: + // Transaction Step 11 - Rollback the transaction. + m_backend.requestTransitToState(SQLTransactionState::CleanupAfterTransactionErrorCallback); + return; } - return SQLTransactionState::RunStatements; + + m_backend.requestTransitToState(SQLTransactionState::RunStatements); } -SQLTransactionState SQLTransaction::deliverQuotaIncreaseCallback() +void SQLTransaction::deliverQuotaIncreaseCallback() { - ASSERT(m_backend->currentStatement()); + ASSERT(m_currentStatement); + ASSERT(!m_shouldRetryCurrentStatement); - bool shouldRetryCurrentStatement = m_database->transactionClient()->didExceedQuota(database()); - m_backend->setShouldRetryCurrentStatement(shouldRetryCurrentStatement); + m_shouldRetryCurrentStatement = m_database->didExceedQuota(); - return SQLTransactionState::RunStatements; + m_backend.requestTransitToState(SQLTransactionState::RunStatements); } -SQLTransactionState SQLTransaction::deliverSuccessCallback() +void SQLTransaction::deliverSuccessCallback() { // Spec 4.3.2.8: Deliver success callback. RefPtr<VoidCallback> successCallback = m_successCallbackWrapper.unwrap(); @@ -223,53 +446,22 @@ SQLTransactionState SQLTransaction::deliverSuccessCallback() // Schedule a "post-success callback" step to return control to the database thread in case there // are further transactions queued up for this Database - return SQLTransactionState::CleanupAndTerminate; + m_backend.requestTransitToState(SQLTransactionState::CleanupAndTerminate); } // This state function is used as a stub function to plug unimplemented states // in the state dispatch table. They are unimplemented because they should // never be reached in the course of correct execution. -SQLTransactionState SQLTransaction::unreachableState() +void SQLTransaction::unreachableState() { ASSERT_NOT_REACHED(); - return SQLTransactionState::End; -} - -SQLTransactionState SQLTransaction::sendToBackendState() -{ - ASSERT(m_nextState != SQLTransactionState::Idle); - m_backend->requestTransitToState(m_nextState); - return SQLTransactionState::Idle; -} - -void SQLTransaction::performPendingCallback() -{ - computeNextStateAndCleanupIfNeeded(); - runStateMachine(); -} - -void SQLTransaction::executeSQL(const String& sqlStatement, const Vector<SQLValue>& arguments, PassRefPtr<SQLStatementCallback> callback, PassRefPtr<SQLStatementErrorCallback> callbackError, ExceptionCode& e) -{ - if (!m_executeSqlAllowed || !m_database->opened()) { - e = INVALID_STATE_ERR; - return; - } - - int permissions = DatabaseAuthorizer::ReadWriteMask; - if (!m_database->databaseContext()->allowDatabaseAccess()) - permissions |= DatabaseAuthorizer::NoAccessMask; - else if (m_readOnly) - permissions |= DatabaseAuthorizer::ReadOnlyMask; - - OwnPtr<SQLStatement> statement = SQLStatement::create(m_database.get(), callback, callbackError); - m_backend->executeSQL(statement.release(), sqlStatement, arguments, permissions); } -bool SQLTransaction::computeNextStateAndCleanupIfNeeded() +void SQLTransaction::computeNextStateAndCleanupIfNeeded() { // Only honor the requested state transition if we're not supposed to be // cleaning up and shutting down: - if (m_database->opened() && !m_database->isInterrupted()) { + if (m_database->opened()) { setStateToRequestedState(); ASSERT(m_nextState == SQLTransactionState::End || m_nextState == SQLTransactionState::DeliverTransactionCallback @@ -279,13 +471,11 @@ bool SQLTransaction::computeNextStateAndCleanupIfNeeded() || m_nextState == SQLTransactionState::DeliverSuccessCallback); LOG(StorageAPI, "Callback %s\n", nameForSQLTransactionState(m_nextState)); - return false; + return; } clearCallbackWrappers(); - m_nextState = SQLTransactionState::CleanupAndTerminate; - - return true; + m_backend.requestTransitToState(SQLTransactionState::CleanupAndTerminate); } void SQLTransaction::clearCallbackWrappers() @@ -296,6 +486,168 @@ void SQLTransaction::clearCallbackWrappers() m_errorCallbackWrapper.clear(); } -} // namespace WebCore +void SQLTransaction::getNextStatement() +{ + m_currentStatement = nullptr; + + LockHolder locker(m_statementMutex); + if (!m_statementQueue.isEmpty()) + m_currentStatement = m_statementQueue.takeFirst(); +} + +bool SQLTransaction::runCurrentStatement() +{ + if (!m_currentStatement) { + // No more statements to run. So move on to the next state. + return false; + } + + m_database->resetAuthorizer(); + + if (m_hasVersionMismatch) + m_currentStatement->setVersionMismatchedError(); + + if (m_currentStatement->execute(m_database)) { + if (m_database->lastActionChangedDatabase()) { + // Flag this transaction as having changed the database for later delegate notification + m_modifiedDatabase = true; + } + + if (m_currentStatement->hasStatementCallback()) { + scheduleCallback(&SQLTransaction::deliverStatementCallback); + return false; + } + + // If we get here, then the statement doesn't have a callback to invoke. + // We can move on to the next statement. Hence, stay in this state. + return true; + } + + if (m_currentStatement->lastExecutionFailedDueToQuota()) { + scheduleCallback(&SQLTransaction::deliverQuotaIncreaseCallback); + return false; + } -#endif // ENABLE(SQL_DATABASE) + handleCurrentStatementError(); + return false; +} + +void SQLTransaction::handleCurrentStatementError() +{ + // Spec 4.3.2.6.6: error - Call the statement's error callback, but if there was no error callback, + // or the transaction was rolled back, jump to the transaction error callback + if (m_currentStatement->hasStatementErrorCallback() && !m_sqliteTransaction->wasRolledBackBySqlite()) { + scheduleCallback(&SQLTransaction::deliverStatementCallback); + return; + } + + m_transactionError = m_currentStatement->sqlError(); + if (!m_transactionError) + m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "the statement failed to execute"); + + handleTransactionError(); +} + +void SQLTransaction::handleTransactionError() +{ + ASSERT(m_transactionError); + if (m_errorCallbackWrapper.hasCallback()) { + scheduleCallback(&SQLTransaction::deliverTransactionErrorCallback); + return; + } + + // No error callback, so fast-forward to the next state and rollback the + // transaction. + m_backend.cleanupAfterTransactionErrorCallback(); +} + +void SQLTransaction::postflightAndCommit() +{ + ASSERT(m_lockAcquired); + + // Spec 4.3.2.7: Perform postflight steps, jumping to the error callback if they fail. + if (m_wrapper && !m_wrapper->performPostflight(*this)) { + m_transactionError = m_wrapper->sqlError(); + if (!m_transactionError) + m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unknown error occurred during transaction postflight"); + + handleTransactionError(); + return; + } + + // Spec 4.3.2.7: Commit the transaction, jumping to the error callback if that fails. + ASSERT(m_sqliteTransaction); + + m_database->disableAuthorizer(); + m_sqliteTransaction->commit(); + m_database->enableAuthorizer(); + + releaseOriginLockIfNeeded(); + + // If the commit failed, the transaction will still be marked as "in progress" + if (m_sqliteTransaction->inProgress()) { + if (m_wrapper) + m_wrapper->handleCommitFailedAfterPostflight(*this); + m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "unable to commit transaction", m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg()); + + handleTransactionError(); + return; + } + + // Vacuum the database if anything was deleted. + if (m_database->hadDeletes()) + m_database->incrementalVacuumIfNeeded(); + + // The commit was successful. If the transaction modified this database, notify the delegates. + if (m_modifiedDatabase) + m_database->didCommitWriteTransaction(); + + // Spec 4.3.2.8: Deliver success callback, if there is one. + scheduleCallback(&SQLTransaction::deliverSuccessCallback); +} + +void SQLTransaction::acquireOriginLock() +{ + ASSERT(!m_originLock); + m_originLock = DatabaseTracker::singleton().originLockFor(m_database->securityOrigin()); + m_originLock->lock(); +} + +void SQLTransaction::releaseOriginLockIfNeeded() +{ + if (m_originLock) { + m_originLock->unlock(); + m_originLock = nullptr; + } +} + +#if !LOG_DISABLED +const char* SQLTransaction::debugStepName(void (SQLTransaction::*step)()) +{ + if (step == &SQLTransaction::acquireLock) + return "acquireLock"; + if (step == &SQLTransaction::openTransactionAndPreflight) + return "openTransactionAndPreflight"; + if (step == &SQLTransaction::runStatements) + return "runStatements"; + if (step == &SQLTransaction::postflightAndCommit) + return "postflightAndCommit"; + if (step == &SQLTransaction::cleanupAfterTransactionErrorCallback) + return "cleanupAfterTransactionErrorCallback"; + if (step == &SQLTransaction::deliverTransactionCallback) + return "deliverTransactionCallback"; + if (step == &SQLTransaction::deliverTransactionErrorCallback) + return "deliverTransactionErrorCallback"; + if (step == &SQLTransaction::deliverStatementCallback) + return "deliverStatementCallback"; + if (step == &SQLTransaction::deliverQuotaIncreaseCallback) + return "deliverQuotaIncreaseCallback"; + if (step == &SQLTransaction::deliverSuccessCallback) + return "deliverSuccessCallback"; + + ASSERT_NOT_REACHED(); + return "UNKNOWN"; +} +#endif + +} // namespace WebCore diff --git a/Source/WebCore/Modules/webdatabase/SQLTransaction.h b/Source/WebCore/Modules/webdatabase/SQLTransaction.h index c24bcc008..81b668f7e 100644 --- a/Source/WebCore/Modules/webdatabase/SQLTransaction.h +++ b/Source/WebCore/Modules/webdatabase/SQLTransaction.h @@ -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. * @@ -26,87 +26,125 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SQLTransaction_h -#define SQLTransaction_h +#pragma once -#if ENABLE(SQL_DATABASE) - -#include "AbstractSQLTransaction.h" +#include "EventTarget.h" +#include "ExceptionOr.h" #include "SQLCallbackWrapper.h" -#include "SQLStatement.h" +#include "SQLTransactionBackend.h" #include "SQLTransactionStateMachine.h" -#include <wtf/PassRefPtr.h> -#include <wtf/RefPtr.h> +#include "SQLValue.h" +#include <wtf/Optional.h> namespace WebCore { -class AbstractSQLTransactionBackend; class Database; class SQLError; class SQLStatementCallback; class SQLStatementErrorCallback; +class SQLTransactionBackend; class SQLTransactionCallback; class SQLTransactionErrorCallback; -class SQLValue; class VoidCallback; -class SQLTransaction : public SQLTransactionStateMachine<SQLTransaction>, public AbstractSQLTransaction { +class SQLTransactionWrapper : public ThreadSafeRefCounted<SQLTransactionWrapper> { public: - static PassRefPtr<SQLTransaction> create(Database*, PassRefPtr<SQLTransactionCallback>, - PassRefPtr<VoidCallback> successCallback, PassRefPtr<SQLTransactionErrorCallback>, - bool readOnly); + virtual ~SQLTransactionWrapper() { } + virtual bool performPreflight(SQLTransaction&) = 0; + virtual bool performPostflight(SQLTransaction&) = 0; + virtual SQLError* sqlError() const = 0; + virtual void handleCommitFailedAfterPostflight(SQLTransaction&) = 0; +}; + +class SQLTransaction : public ThreadSafeRefCounted<SQLTransaction>, public SQLTransactionStateMachine<SQLTransaction> { +public: + static Ref<SQLTransaction> create(Ref<Database>&&, RefPtr<SQLTransactionCallback>&&, RefPtr<VoidCallback>&& successCallback, RefPtr<SQLTransactionErrorCallback>&&, RefPtr<SQLTransactionWrapper>&&, bool readOnly); + ~SQLTransaction(); + + ExceptionOr<void> executeSql(const String& sqlStatement, std::optional<Vector<SQLValue>>&& arguments, RefPtr<SQLStatementCallback>&&, RefPtr<SQLStatementErrorCallback>&&); + void lockAcquired(); + void performNextStep(); void performPendingCallback(); - void executeSQL(const String& sqlStatement, const Vector<SQLValue>& arguments, - PassRefPtr<SQLStatementCallback>, PassRefPtr<SQLStatementErrorCallback>, ExceptionCode&); + Database& database() { return m_database; } + bool isReadOnly() const { return m_readOnly; } + void notifyDatabaseThreadIsShuttingDown(); - Database* database() { return m_database.get(); } + // APIs called from the backend published via SQLTransaction: + void requestTransitToState(SQLTransactionState); private: - SQLTransaction(Database*, PassRefPtr<SQLTransactionCallback>, - PassRefPtr<VoidCallback> successCallback, PassRefPtr<SQLTransactionErrorCallback>, - bool readOnly); + friend class SQLTransactionBackend; + + SQLTransaction(Ref<Database>&&, RefPtr<SQLTransactionCallback>&&, RefPtr<VoidCallback>&& successCallback, RefPtr<SQLTransactionErrorCallback>&&, RefPtr<SQLTransactionWrapper>&&, bool readOnly); + + void enqueueStatement(std::unique_ptr<SQLStatement>); + + void checkAndHandleClosedDatabase(); void clearCallbackWrappers(); - // APIs called from the backend published via AbstractSQLTransaction: - virtual void requestTransitToState(SQLTransactionState) override; - virtual bool hasCallback() const override; - virtual bool hasSuccessCallback() const override; - virtual bool hasErrorCallback() const override; - virtual void setBackend(AbstractSQLTransactionBackend*) override; + void scheduleCallback(void (SQLTransaction::*)()); // State Machine functions: - virtual StateFunction stateFunctionFor(SQLTransactionState) override; - bool computeNextStateAndCleanupIfNeeded(); + StateFunction stateFunctionFor(SQLTransactionState) override; + void computeNextStateAndCleanupIfNeeded(); // State functions: - SQLTransactionState deliverTransactionCallback(); - SQLTransactionState deliverTransactionErrorCallback(); - SQLTransactionState deliverStatementCallback(); - SQLTransactionState deliverQuotaIncreaseCallback(); - SQLTransactionState deliverSuccessCallback(); - - SQLTransactionState unreachableState(); - SQLTransactionState sendToBackendState(); - - SQLTransactionState nextStateForTransactionError(); + void acquireLock(); + void openTransactionAndPreflight(); + void runStatements(); + void cleanupAndTerminate(); + void cleanupAfterTransactionErrorCallback(); + void deliverTransactionCallback(); + void deliverTransactionErrorCallback(); + void deliverStatementCallback(); + void deliverQuotaIncreaseCallback(); + void deliverSuccessCallback(); + + NO_RETURN_DUE_TO_ASSERT void unreachableState(); + + void getNextStatement(); + bool runCurrentStatement(); + void handleCurrentStatementError(); + void handleTransactionError(); + void postflightAndCommit(); + + void acquireOriginLock(); + void releaseOriginLockIfNeeded(); + +#if !LOG_DISABLED + static const char* debugStepName(void (SQLTransaction::*)()); +#endif - RefPtr<Database> m_database; - RefPtr<AbstractSQLTransactionBackend> m_backend; + Ref<Database> m_database; SQLCallbackWrapper<SQLTransactionCallback> m_callbackWrapper; SQLCallbackWrapper<VoidCallback> m_successCallbackWrapper; SQLCallbackWrapper<SQLTransactionErrorCallback> m_errorCallbackWrapper; - bool m_executeSqlAllowed; + RefPtr<SQLTransactionWrapper> m_wrapper; + + void (SQLTransaction::*m_nextStep)(); + + bool m_executeSqlAllowed { false }; RefPtr<SQLError> m_transactionError; - bool m_readOnly; -}; + bool m_shouldRetryCurrentStatement { false }; + bool m_modifiedDatabase { false }; + bool m_lockAcquired { false }; + bool m_readOnly { false }; + bool m_hasVersionMismatch { false }; -} // namespace WebCore + Lock m_statementMutex; + Deque<std::unique_ptr<SQLStatement>> m_statementQueue; -#endif + std::unique_ptr<SQLStatement> m_currentStatement; + + std::unique_ptr<SQLiteTransaction> m_sqliteTransaction; + RefPtr<OriginLock> m_originLock; -#endif // SQLTransaction_h + SQLTransactionBackend m_backend; +}; + +} // namespace WebCore diff --git a/Source/WebCore/Modules/webdatabase/SQLTransaction.idl b/Source/WebCore/Modules/webdatabase/SQLTransaction.idl index 2eaecd2c4..1f53a1fe8 100644 --- a/Source/WebCore/Modules/webdatabase/SQLTransaction.idl +++ b/Source/WebCore/Modules/webdatabase/SQLTransaction.idl @@ -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. * @@ -26,14 +26,10 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +typedef (DOMString? or unrestricted double) SQLValue; + [ - NoInterfaceObject, - Conditional=SQL_DATABASE, - JSNoStaticTables, SkipVTableValidation, ] interface SQLTransaction { - [Custom] void executeSql(DOMString sqlStatement, - ObjectArray arguments, - optional SQLStatementCallback callback, - optional SQLStatementErrorCallback errorCallback); + [MayThrowException] void executeSql(DOMString sqlStatement, optional sequence<SQLValue>? arguments = [], optional SQLStatementCallback? callback, optional SQLStatementErrorCallback? errorCallback); }; diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionBackend.cpp b/Source/WebCore/Modules/webdatabase/SQLTransactionBackend.cpp index 244f28566..2ea93ddf2 100644 --- a/Source/WebCore/Modules/webdatabase/SQLTransactionBackend.cpp +++ b/Source/WebCore/Modules/webdatabase/SQLTransactionBackend.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,23 +29,19 @@ #include "config.h" #include "SQLTransactionBackend.h" -#if ENABLE(SQL_DATABASE) - -#include "AbstractSQLTransaction.h" -#include "Database.h" // FIXME: Should only be used in the frontend. +#include "Database.h" #include "DatabaseAuthorizer.h" -#include "DatabaseBackend.h" -#include "DatabaseBackendContext.h" +#include "DatabaseContext.h" #include "DatabaseThread.h" #include "DatabaseTracker.h" -#include "ExceptionCode.h" #include "Logging.h" #include "OriginLock.h" #include "SQLError.h" -#include "SQLStatementBackend.h" -#include "SQLTransactionClient.h" +#include "SQLStatement.h" +#include "SQLStatementCallback.h" +#include "SQLStatementErrorCallback.h" +#include "SQLTransaction.h" #include "SQLTransactionCoordinator.h" -#include "SQLValue.h" #include "SQLiteTransaction.h" #include <wtf/StdLibExtras.h> #include <wtf/text/WTFString.h> @@ -260,7 +256,7 @@ // // When executing the transaction (in DatabaseThread::databaseThread()): // ==================================================================== -// OwnPtr<DatabaseTask> task; // points to ... +// std::unique_ptr<DatabaseTask> task; // points to ... // --> DatabaseTransactionTask // RefPtr<SQLTransactionBackend> m_transaction points to ... // --> SQLTransactionBackend // RefPtr<SQLTransaction> m_frontend; // --> SQLTransaction // RefPtr<SQLTransactionBackend> m_backend points to ... @@ -284,7 +280,7 @@ // However, there will still be a DatabaseTask pointing to the SQLTransactionBackend (see // the "When executing the transaction" chain above). This will keep the // SQLTransactionBackend alive until DatabaseThread::databaseThread() releases its -// task OwnPtr. +// task std::unique_ptr. // // What happens if a transaction is interrupted? // ============================================ @@ -344,61 +340,38 @@ namespace WebCore { -PassRefPtr<SQLTransactionBackend> SQLTransactionBackend::create(DatabaseBackend* db, - PassRefPtr<AbstractSQLTransaction> frontend, PassRefPtr<SQLTransactionWrapper> wrapper, bool readOnly) -{ - return adoptRef(new SQLTransactionBackend(db, frontend, wrapper, readOnly)); -} - -SQLTransactionBackend::SQLTransactionBackend(DatabaseBackend* db, - PassRefPtr<AbstractSQLTransaction> frontend, PassRefPtr<SQLTransactionWrapper> wrapper, bool readOnly) +SQLTransactionBackend::SQLTransactionBackend(SQLTransaction& frontend) : m_frontend(frontend) - , m_database(db) - , m_wrapper(wrapper) - , m_hasCallback(m_frontend->hasCallback()) - , m_hasSuccessCallback(m_frontend->hasSuccessCallback()) - , m_hasErrorCallback(m_frontend->hasErrorCallback()) - , m_shouldRetryCurrentStatement(false) - , m_modifiedDatabase(false) - , m_lockAcquired(false) - , m_readOnly(readOnly) - , m_hasVersionMismatch(false) { - ASSERT(m_database); - m_frontend->setBackend(this); m_requestedState = SQLTransactionState::AcquireLock; } SQLTransactionBackend::~SQLTransactionBackend() { - ASSERT(!m_sqliteTransaction); + ASSERT(!m_frontend.m_sqliteTransaction); } void SQLTransactionBackend::doCleanup() { - if (!m_frontend) - return; - m_frontend = 0; // Break the reference cycle. See comment about the life-cycle above. - - ASSERT(currentThread() == database()->databaseContext()->databaseThread()->getThreadID()); + ASSERT(currentThread() == m_frontend.database().databaseThread().getThreadID()); - releaseOriginLockIfNeeded(); + m_frontend.releaseOriginLockIfNeeded(); - MutexLocker locker(m_statementMutex); - m_statementQueue.clear(); + LockHolder locker(m_frontend.m_statementMutex); + m_frontend.m_statementQueue.clear(); - if (m_sqliteTransaction) { + if (m_frontend.m_sqliteTransaction) { // In the event we got here because of an interruption or error (i.e. if // the transaction is in progress), we should roll it back here. Clearing // m_sqliteTransaction invokes SQLiteTransaction's destructor which does // just that. We might as well do this unconditionally and free up its // resources because we're already terminating. - m_sqliteTransaction.clear(); + m_frontend.m_sqliteTransaction = nullptr; } // Release the lock on this database - if (m_lockAcquired) - m_database->transactionCoordinator()->releaseLock(this); + if (m_frontend.m_lockAcquired) + m_frontend.m_database->transactionCoordinator()->releaseLock(m_frontend); // Do some aggresive clean up here except for m_database. // @@ -423,23 +396,7 @@ void SQLTransactionBackend::doCleanup() // SQLTransactionBackend is guaranteed to not destruct until the frontend // is also destructing. - m_wrapper = 0; -} - -AbstractSQLStatement* SQLTransactionBackend::currentStatement() -{ - return m_currentStatementBackend->frontend(); -} - -PassRefPtr<SQLError> SQLTransactionBackend::transactionError() -{ - return m_transactionError; -} - -void SQLTransactionBackend::setShouldRetryCurrentStatement(bool shouldRetry) -{ - ASSERT(!m_shouldRetryCurrentStatement); - m_shouldRetryCurrentStatement = shouldRetry; + m_frontend.m_wrapper = nullptr; } SQLTransactionBackend::StateFunction SQLTransactionBackend::stateFunctionFor(SQLTransactionState state) @@ -450,14 +407,14 @@ SQLTransactionBackend::StateFunction SQLTransactionBackend::stateFunctionFor(SQL &SQLTransactionBackend::acquireLock, // 2. &SQLTransactionBackend::openTransactionAndPreflight, // 3. &SQLTransactionBackend::runStatements, // 4. - &SQLTransactionBackend::postflightAndCommit, // 5. + &SQLTransactionBackend::unreachableState, // 5. postflightAndCommit &SQLTransactionBackend::cleanupAndTerminate, // 6. &SQLTransactionBackend::cleanupAfterTransactionErrorCallback, // 7. - &SQLTransactionBackend::sendToFrontendState, // 8. deliverTransactionCallback - &SQLTransactionBackend::sendToFrontendState, // 9. deliverTransactionErrorCallback - &SQLTransactionBackend::sendToFrontendState, // 10. deliverStatementCallback - &SQLTransactionBackend::sendToFrontendState, // 11. deliverQuotaIncreaseCallback - &SQLTransactionBackend::sendToFrontendState // 12. deliverSuccessCallback + &SQLTransactionBackend::unreachableState, // 8. deliverTransactionCallback + &SQLTransactionBackend::unreachableState, // 9. deliverTransactionErrorCallback + &SQLTransactionBackend::unreachableState, // 10. deliverStatementCallback + &SQLTransactionBackend::unreachableState, // 11. deliverQuotaIncreaseCallback + &SQLTransactionBackend::unreachableState // 12. deliverSuccessCallback }; ASSERT(WTF_ARRAY_LENGTH(stateFunctions) == static_cast<int>(SQLTransactionState::NumberOfStates)); @@ -466,17 +423,11 @@ SQLTransactionBackend::StateFunction SQLTransactionBackend::stateFunctionFor(SQL return stateFunctions[static_cast<int>(state)]; } -void SQLTransactionBackend::enqueueStatementBackend(PassRefPtr<SQLStatementBackend> statementBackend) -{ - MutexLocker locker(m_statementMutex); - m_statementQueue.append(statementBackend); -} - void SQLTransactionBackend::computeNextStateAndCleanupIfNeeded() { // Only honor the requested state transition if we're not supposed to be // cleaning up and shutting down: - if (m_database->opened() && !m_database->isInterrupted()) { + if (m_frontend.m_database->opened()) { setStateToRequestedState(); ASSERT(m_nextState == SQLTransactionState::AcquireLock || m_nextState == SQLTransactionState::OpenTransactionAndPreflight @@ -498,49 +449,23 @@ void SQLTransactionBackend::computeNextStateAndCleanupIfNeeded() LOG(StorageAPI, "Database was stopped or interrupted - cancelling work for this transaction"); // The current SQLite transaction should be stopped, as well - if (m_sqliteTransaction) { - m_sqliteTransaction->stop(); - m_sqliteTransaction.clear(); + if (m_frontend.m_sqliteTransaction) { + m_frontend.m_sqliteTransaction->stop(); + m_frontend.m_sqliteTransaction = nullptr; } // Terminate the frontend state machine. This also gets the frontend to // call computeNextStateAndCleanupIfNeeded() and clear its wrappers // if needed. - m_frontend->requestTransitToState(SQLTransactionState::End); + m_frontend.requestTransitToState(SQLTransactionState::End); // Redirect to the end state to abort, clean up, and end the transaction. doCleanup(); } -void SQLTransactionBackend::performNextStep() -{ - computeNextStateAndCleanupIfNeeded(); - runStateMachine(); -} - -#if PLATFORM(IOS) -bool SQLTransactionBackend::shouldPerformWhilePaused() const -{ - // SQLTransactions should only run-while-paused if they have progressed passed the first transaction step. - return m_nextState != SQLTransactionState::AcquireLock; -} -#endif - -void SQLTransactionBackend::executeSQL(PassOwnPtr<AbstractSQLStatement> statement, - const String& sqlStatement, const Vector<SQLValue>& arguments, int permissions) -{ - RefPtr<SQLStatementBackend> statementBackend; - statementBackend = SQLStatementBackend::create(statement, sqlStatement, arguments, permissions); - - if (Database::from(m_database.get())->deleted()) - statementBackend->setDatabaseDeletedError(); - - enqueueStatementBackend(statementBackend); -} - void SQLTransactionBackend::notifyDatabaseThreadIsShuttingDown() { - ASSERT(currentThread() == database()->databaseContext()->databaseThread()->getThreadID()); + ASSERT(currentThread() == m_frontend.database().databaseThread().getThreadID()); // If the transaction is in progress, we should roll it back here, since this // is our last opportunity to do something related to this transaction on the @@ -550,264 +475,29 @@ void SQLTransactionBackend::notifyDatabaseThreadIsShuttingDown() doCleanup(); } -SQLTransactionState SQLTransactionBackend::acquireLock() -{ - m_database->transactionCoordinator()->acquireLock(this); - return SQLTransactionState::Idle; -} - -void SQLTransactionBackend::lockAcquired() -{ - m_lockAcquired = true; - requestTransitToState(SQLTransactionState::OpenTransactionAndPreflight); -} - -SQLTransactionState SQLTransactionBackend::openTransactionAndPreflight() -{ - ASSERT(!m_database->sqliteDatabase().transactionInProgress()); - ASSERT(m_lockAcquired); - - LOG(StorageAPI, "Opening and preflighting transaction %p", this); - - // If the database was deleted, jump to the error callback - if (Database::from(m_database.get())->deleted()) { - m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unable to open a transaction, because the user deleted the database"); - return nextStateForTransactionError(); - } - - // Set the maximum usage for this transaction if this transactions is not read-only - if (!m_readOnly) { - acquireOriginLock(); - m_database->sqliteDatabase().setMaximumSize(m_database->maximumSize()); - } - - ASSERT(!m_sqliteTransaction); - m_sqliteTransaction = adoptPtr(new SQLiteTransaction(m_database->sqliteDatabase(), m_readOnly)); - - m_database->resetDeletes(); - m_database->disableAuthorizer(); - m_sqliteTransaction->begin(); - m_database->enableAuthorizer(); - - // Spec 4.3.2.1+2: Open a transaction to the database, jumping to the error callback if that fails - if (!m_sqliteTransaction->inProgress()) { - ASSERT(!m_database->sqliteDatabase().transactionInProgress()); - m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "unable to begin transaction", - m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg()); - m_sqliteTransaction.clear(); - return nextStateForTransactionError(); - } - - // Note: We intentionally retrieve the actual version even with an empty expected version. - // In multi-process browsers, we take this opportinutiy to update the cached value for - // the actual version. In single-process browsers, this is just a map lookup. - String actualVersion; - if (!m_database->getActualVersionForTransaction(actualVersion)) { - m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "unable to read version", - m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg()); - m_database->disableAuthorizer(); - m_sqliteTransaction.clear(); - m_database->enableAuthorizer(); - return nextStateForTransactionError(); - } - m_hasVersionMismatch = !m_database->expectedVersion().isEmpty() && (m_database->expectedVersion() != actualVersion); - - // Spec 4.3.2.3: Perform preflight steps, jumping to the error callback if they fail - if (m_wrapper && !m_wrapper->performPreflight(this)) { - m_database->disableAuthorizer(); - m_sqliteTransaction.clear(); - m_database->enableAuthorizer(); - m_transactionError = m_wrapper->sqlError(); - if (!m_transactionError) - m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unknown error occurred during transaction preflight"); - return nextStateForTransactionError(); - } - - // Spec 4.3.2.4: Invoke the transaction callback with the new SQLTransaction object - if (m_hasCallback) - return SQLTransactionState::DeliverTransactionCallback; - - // If we have no callback to make, skip pass to the state after: - return SQLTransactionState::RunStatements; -} - -SQLTransactionState SQLTransactionBackend::runStatements() +void SQLTransactionBackend::acquireLock() { - ASSERT(m_lockAcquired); - SQLTransactionState nextState; - - // If there is a series of statements queued up that are all successful and have no associated - // SQLStatementCallback objects, then we can burn through the queue - do { - if (m_shouldRetryCurrentStatement && !m_sqliteTransaction->wasRolledBackBySqlite()) { - m_shouldRetryCurrentStatement = false; - // FIXME - Another place that needs fixing up after <rdar://problem/5628468> is addressed. - // See ::openTransactionAndPreflight() for discussion - - // Reset the maximum size here, as it was increased to allow us to retry this statement. - // m_shouldRetryCurrentStatement is set to true only when a statement exceeds - // the quota, which can happen only in a read-write transaction. Therefore, there - // is no need to check here if the transaction is read-write. - m_database->sqliteDatabase().setMaximumSize(m_database->maximumSize()); - } else { - // If the current statement has already been run, failed due to quota constraints, and we're not retrying it, - // that means it ended in an error. Handle it now - if (m_currentStatementBackend && m_currentStatementBackend->lastExecutionFailedDueToQuota()) { - return nextStateForCurrentStatementError(); - } - - // Otherwise, advance to the next statement - getNextStatement(); - } - nextState = runCurrentStatementAndGetNextState(); - } while (nextState == SQLTransactionState::RunStatements); - - return nextState; + m_frontend.acquireLock(); } -void SQLTransactionBackend::getNextStatement() +void SQLTransactionBackend::openTransactionAndPreflight() { - m_currentStatementBackend = 0; - - MutexLocker locker(m_statementMutex); - if (!m_statementQueue.isEmpty()) - m_currentStatementBackend = m_statementQueue.takeFirst(); -} - -SQLTransactionState SQLTransactionBackend::runCurrentStatementAndGetNextState() -{ - if (!m_currentStatementBackend) { - // No more statements to run. So move on to the next state. - return SQLTransactionState::PostflightAndCommit; - } - - m_database->resetAuthorizer(); - - if (m_hasVersionMismatch) - m_currentStatementBackend->setVersionMismatchedError(); - - if (m_currentStatementBackend->execute(m_database.get())) { - if (m_database->lastActionChangedDatabase()) { - // Flag this transaction as having changed the database for later delegate notification - m_modifiedDatabase = true; - } - - if (m_currentStatementBackend->hasStatementCallback()) { - return SQLTransactionState::DeliverStatementCallback; - } - - // If we get here, then the statement doesn't have a callback to invoke. - // We can move on to the next statement. Hence, stay in this state. - return SQLTransactionState::RunStatements; - } - - if (m_currentStatementBackend->lastExecutionFailedDueToQuota()) { - return SQLTransactionState::DeliverQuotaIncreaseCallback; - } - - return nextStateForCurrentStatementError(); -} - -SQLTransactionState SQLTransactionBackend::nextStateForCurrentStatementError() -{ - // Spec 4.3.2.6.6: error - Call the statement's error callback, but if there was no error callback, - // or the transaction was rolled back, jump to the transaction error callback - if (m_currentStatementBackend->hasStatementErrorCallback() && !m_sqliteTransaction->wasRolledBackBySqlite()) - return SQLTransactionState::DeliverStatementCallback; - - m_transactionError = m_currentStatementBackend->sqlError(); - if (!m_transactionError) - m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "the statement failed to execute"); - return nextStateForTransactionError(); -} - -SQLTransactionState SQLTransactionBackend::postflightAndCommit() -{ - ASSERT(m_lockAcquired); - - // Spec 4.3.2.7: Perform postflight steps, jumping to the error callback if they fail. - if (m_wrapper && !m_wrapper->performPostflight(this)) { - m_transactionError = m_wrapper->sqlError(); - if (!m_transactionError) - m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unknown error occurred during transaction postflight"); - return nextStateForTransactionError(); - } - - // Spec 4.3.2.7: Commit the transaction, jumping to the error callback if that fails. - ASSERT(m_sqliteTransaction); - - m_database->disableAuthorizer(); - m_sqliteTransaction->commit(); - m_database->enableAuthorizer(); - - releaseOriginLockIfNeeded(); - - // If the commit failed, the transaction will still be marked as "in progress" - if (m_sqliteTransaction->inProgress()) { - if (m_wrapper) - m_wrapper->handleCommitFailedAfterPostflight(this); - m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "unable to commit transaction", - m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg()); - return nextStateForTransactionError(); - } - - // Vacuum the database if anything was deleted. - if (m_database->hadDeletes()) - m_database->incrementalVacuumIfNeeded(); - - // The commit was successful. If the transaction modified this database, notify the delegates. - if (m_modifiedDatabase) - m_database->transactionClient()->didCommitWriteTransaction(database()); - - // Spec 4.3.2.8: Deliver success callback, if there is one. - return SQLTransactionState::DeliverSuccessCallback; + m_frontend.openTransactionAndPreflight(); } -SQLTransactionState SQLTransactionBackend::cleanupAndTerminate() +void SQLTransactionBackend::runStatements() { - ASSERT(m_lockAcquired); - - // Spec 4.3.2.9: End transaction steps. There is no next step. - LOG(StorageAPI, "Transaction %p is complete\n", this); - ASSERT(!m_database->sqliteDatabase().transactionInProgress()); - - // Phase 5 cleanup. See comment on the SQLTransaction life-cycle above. - doCleanup(); - m_database->inProgressTransactionCompleted(); - return SQLTransactionState::End; + m_frontend.runStatements(); } -SQLTransactionState SQLTransactionBackend::nextStateForTransactionError() +void SQLTransactionBackend::cleanupAndTerminate() { - ASSERT(m_transactionError); - if (m_hasErrorCallback) - return SQLTransactionState::DeliverTransactionErrorCallback; - - // No error callback, so fast-forward to the next state and rollback the - // transaction. - return SQLTransactionState::CleanupAfterTransactionErrorCallback; + m_frontend.cleanupAndTerminate(); } -SQLTransactionState SQLTransactionBackend::cleanupAfterTransactionErrorCallback() +void SQLTransactionBackend::cleanupAfterTransactionErrorCallback() { - ASSERT(m_lockAcquired); - - LOG(StorageAPI, "Transaction %p is complete with an error\n", this); - m_database->disableAuthorizer(); - if (m_sqliteTransaction) { - // Spec 4.3.2.10: Rollback the transaction. - m_sqliteTransaction->rollback(); - - ASSERT(!m_database->sqliteDatabase().transactionInProgress()); - m_sqliteTransaction.clear(); - } - m_database->enableAuthorizer(); - - releaseOriginLockIfNeeded(); - - ASSERT(!m_database->sqliteDatabase().transactionInProgress()); - - return SQLTransactionState::CleanupAndTerminate; + m_frontend.cleanupAfterTransactionErrorCallback(); } // requestTransitToState() can be called from the frontend. Hence, it should @@ -818,40 +508,15 @@ void SQLTransactionBackend::requestTransitToState(SQLTransactionState nextState) LOG(StorageAPI, "Scheduling %s for transaction %p\n", nameForSQLTransactionState(nextState), this); m_requestedState = nextState; ASSERT(m_requestedState != SQLTransactionState::End); - m_database->scheduleTransactionStep(this); + m_frontend.m_database->scheduleTransactionStep(m_frontend); } // This state function is used as a stub function to plug unimplemented states // in the state dispatch table. They are unimplemented because they should // never be reached in the course of correct execution. -SQLTransactionState SQLTransactionBackend::unreachableState() +void SQLTransactionBackend::unreachableState() { ASSERT_NOT_REACHED(); - return SQLTransactionState::End; -} - -SQLTransactionState SQLTransactionBackend::sendToFrontendState() -{ - ASSERT(m_nextState != SQLTransactionState::Idle); - m_frontend->requestTransitToState(m_nextState); - return SQLTransactionState::Idle; -} - -void SQLTransactionBackend::acquireOriginLock() -{ - ASSERT(!m_originLock); - m_originLock = DatabaseTracker::tracker().originLockFor(m_database->securityOrigin()); - m_originLock->lock(); -} - -void SQLTransactionBackend::releaseOriginLockIfNeeded() -{ - if (m_originLock) { - m_originLock->unlock(); - m_originLock.clear(); - } } } // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionBackend.h b/Source/WebCore/Modules/webdatabase/SQLTransactionBackend.h index 17a4c65f2..29e603c95 100644 --- a/Source/WebCore/Modules/webdatabase/SQLTransactionBackend.h +++ b/Source/WebCore/Modules/webdatabase/SQLTransactionBackend.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2013, 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -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. * @@ -25,122 +25,55 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SQLTransactionBackend_h -#define SQLTransactionBackend_h -#if ENABLE(SQL_DATABASE) +#pragma once -#include "AbstractSQLStatement.h" -#include "AbstractSQLTransactionBackend.h" -#include "DatabaseBasicTypes.h" #include "SQLTransactionStateMachine.h" +#include <memory> #include <wtf/Deque.h> #include <wtf/Forward.h> +#include <wtf/Lock.h> #include <wtf/text/WTFString.h> namespace WebCore { -class AbstractSQLTransaction; -class DatabaseBackend; +class Database; class OriginLock; class SQLError; class SQLiteTransaction; -class SQLStatementBackend; -class SQLTransactionBackend; -class SQLValue; +class SQLStatement; +class SQLTransaction; +class SQLTransactionWrapper; -class SQLTransactionWrapper : public ThreadSafeRefCounted<SQLTransactionWrapper> { +class SQLTransactionBackend : public SQLTransactionStateMachine<SQLTransactionBackend> { public: - virtual ~SQLTransactionWrapper() { } - virtual bool performPreflight(SQLTransactionBackend*) = 0; - virtual bool performPostflight(SQLTransactionBackend*) = 0; - virtual SQLError* sqlError() const = 0; - virtual void handleCommitFailedAfterPostflight(SQLTransactionBackend*) = 0; -}; - -class SQLTransactionBackend : public SQLTransactionStateMachine<SQLTransactionBackend>, public AbstractSQLTransactionBackend { -public: - static PassRefPtr<SQLTransactionBackend> create(DatabaseBackend*, - PassRefPtr<AbstractSQLTransaction>, PassRefPtr<SQLTransactionWrapper>, bool readOnly); - - virtual ~SQLTransactionBackend(); - - void lockAcquired(); - void performNextStep(); + explicit SQLTransactionBackend(SQLTransaction&); + ~SQLTransactionBackend(); -#if PLATFORM(IOS) - bool shouldPerformWhilePaused() const; -#endif - - DatabaseBackend* database() { return m_database.get(); } - bool isReadOnly() { return m_readOnly; } void notifyDatabaseThreadIsShuttingDown(); -private: - SQLTransactionBackend(DatabaseBackend*, PassRefPtr<AbstractSQLTransaction>, - PassRefPtr<SQLTransactionWrapper>, bool readOnly); + // API called from the frontend published via SQLTransactionBackend: + void requestTransitToState(SQLTransactionState); - // APIs called from the frontend published via AbstractSQLTransactionBackend: - virtual void requestTransitToState(SQLTransactionState) override; - virtual PassRefPtr<SQLError> transactionError() override; - virtual AbstractSQLStatement* currentStatement() override; - virtual void setShouldRetryCurrentStatement(bool) override; - virtual void executeSQL(PassOwnPtr<AbstractSQLStatement>, const String& statement, - const Vector<SQLValue>& arguments, int permissions) override; +private: + friend class SQLTransaction; void doCleanup(); - void enqueueStatementBackend(PassRefPtr<SQLStatementBackend>); - // State Machine functions: - virtual StateFunction stateFunctionFor(SQLTransactionState) override; + StateFunction stateFunctionFor(SQLTransactionState) override; void computeNextStateAndCleanupIfNeeded(); // State functions: - SQLTransactionState acquireLock(); - SQLTransactionState openTransactionAndPreflight(); - SQLTransactionState runStatements(); - SQLTransactionState postflightAndCommit(); - SQLTransactionState cleanupAndTerminate(); - SQLTransactionState cleanupAfterTransactionErrorCallback(); + void acquireLock(); + void openTransactionAndPreflight(); + void runStatements(); + void cleanupAndTerminate(); + void cleanupAfterTransactionErrorCallback(); - SQLTransactionState unreachableState(); - SQLTransactionState sendToFrontendState(); + NO_RETURN_DUE_TO_ASSERT void unreachableState(); - SQLTransactionState nextStateForCurrentStatementError(); - SQLTransactionState nextStateForTransactionError(); - SQLTransactionState runCurrentStatementAndGetNextState(); - - void getNextStatement(); - - void acquireOriginLock(); - void releaseOriginLockIfNeeded(); - - RefPtr<AbstractSQLTransaction> m_frontend; // Has a reference cycle, and will break in doCleanup(). - RefPtr<SQLStatementBackend> m_currentStatementBackend; - - RefPtr<DatabaseBackend> m_database; - RefPtr<SQLTransactionWrapper> m_wrapper; - RefPtr<SQLError> m_transactionError; - - bool m_hasCallback; - bool m_hasSuccessCallback; - bool m_hasErrorCallback; - bool m_shouldRetryCurrentStatement; - bool m_modifiedDatabase; - bool m_lockAcquired; - bool m_readOnly; - bool m_hasVersionMismatch; - - Mutex m_statementMutex; - Deque<RefPtr<SQLStatementBackend>> m_statementQueue; - - OwnPtr<SQLiteTransaction> m_sqliteTransaction; - RefPtr<OriginLock> m_originLock; + SQLTransaction& m_frontend; }; } // namespace WebCore - -#endif - -#endif // SQLTransactionBackend_h diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionBackendSync.cpp b/Source/WebCore/Modules/webdatabase/SQLTransactionBackendSync.cpp deleted file mode 100644 index 552226405..000000000 --- a/Source/WebCore/Modules/webdatabase/SQLTransactionBackendSync.cpp +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * 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. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR 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. - */ - -#include "config.h" -#include "SQLTransactionBackendSync.h" - -#if ENABLE(SQL_DATABASE) - -#include "DatabaseAuthorizer.h" -#include "DatabaseBackendContext.h" -#include "DatabaseSync.h" -#include "SQLException.h" -#include "SQLResultSet.h" -#include "SQLStatementSync.h" -#include "SQLTransactionClient.h" -#include "SQLTransactionSync.h" -#include "SQLTransactionSyncCallback.h" -#include "SQLValue.h" -#include "SQLiteTransaction.h" -#include "ScriptExecutionContext.h" -#include <wtf/PassRefPtr.h> -#include <wtf/RefPtr.h> -#include <wtf/text/WTFString.h> - -namespace WebCore { - -SQLTransactionBackendSync::SQLTransactionBackendSync(DatabaseSync* db, PassRefPtr<SQLTransactionSyncCallback> callback, bool readOnly) - : m_database(db) - , m_callback(callback) - , m_readOnly(readOnly) - , m_hasVersionMismatch(false) - , m_modifiedDatabase(false) - , m_transactionClient(adoptPtr(new SQLTransactionClient())) -{ - ASSERT(m_database->scriptExecutionContext()->isContextThread()); -} - -SQLTransactionBackendSync::~SQLTransactionBackendSync() -{ - ASSERT(m_database->scriptExecutionContext()->isContextThread()); - if (m_sqliteTransaction && m_sqliteTransaction->inProgress()) - rollback(); -} - -PassRefPtr<SQLResultSet> SQLTransactionBackendSync::executeSQL(const String& sqlStatement, const Vector<SQLValue>& arguments, ExceptionCode& ec) -{ - ASSERT(m_database->scriptExecutionContext()->isContextThread()); - - m_database->setLastErrorMessage(""); - - if (!m_database->opened()) { - m_database->setLastErrorMessage("cannot executeSQL because the database is not open"); - ec = SQLException::UNKNOWN_ERR; - return 0; - } - - if (m_hasVersionMismatch) { - m_database->setLastErrorMessage("cannot executeSQL because there is a version mismatch"); - ec = SQLException::VERSION_ERR; - return 0; - } - - if (sqlStatement.isEmpty()) - return 0; - - int permissions = DatabaseAuthorizer::ReadWriteMask; - if (!m_database->databaseContext()->allowDatabaseAccess()) - permissions |= DatabaseAuthorizer::NoAccessMask; - else if (m_readOnly) - permissions |= DatabaseAuthorizer::ReadOnlyMask; - - SQLStatementSync statement(sqlStatement, arguments, permissions); - - m_database->resetAuthorizer(); - bool retryStatement = true; - RefPtr<SQLResultSet> resultSet; - while (retryStatement) { - retryStatement = false; - resultSet = statement.execute(m_database.get(), ec); - if (!resultSet) { - if (m_sqliteTransaction->wasRolledBackBySqlite()) - return 0; - - if (ec == SQLException::QUOTA_ERR) { - if (m_transactionClient->didExceedQuota(database())) { - ec = 0; - retryStatement = true; - } else { - m_database->setLastErrorMessage("there was not enough remaining storage space"); - return 0; - } - } - } - } - - if (m_database->lastActionChangedDatabase()) - m_modifiedDatabase = true; - - return resultSet.release(); -} - -ExceptionCode SQLTransactionBackendSync::begin() -{ - ASSERT(m_database->scriptExecutionContext()->isContextThread()); - if (!m_database->opened()) { - m_database->setLastErrorMessage("cannot begin transaction because the database is not open"); - return SQLException::UNKNOWN_ERR; - } - - ASSERT(!m_database->sqliteDatabase().transactionInProgress()); - - // Set the maximum usage for this transaction if this transactions is not read-only. - if (!m_readOnly) - m_database->sqliteDatabase().setMaximumSize(m_database->maximumSize()); - - ASSERT(!m_sqliteTransaction); - m_sqliteTransaction = adoptPtr(new SQLiteTransaction(m_database->sqliteDatabase(), m_readOnly)); - - m_database->resetDeletes(); - m_database->disableAuthorizer(); - m_sqliteTransaction->begin(); - m_database->enableAuthorizer(); - - // Check if begin() succeeded. - if (!m_sqliteTransaction->inProgress()) { - ASSERT(!m_database->sqliteDatabase().transactionInProgress()); - m_database->setLastErrorMessage("unable to begin transaction", - m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg()); - m_sqliteTransaction.clear(); - return SQLException::DATABASE_ERR; - } - - // Note: We intentionally retrieve the actual version even with an empty expected version. - // In multi-process browsers, we take this opportinutiy to update the cached value for - // the actual version. In single-process browsers, this is just a map lookup. - String actualVersion; - if (!m_database->getActualVersionForTransaction(actualVersion)) { - m_database->setLastErrorMessage("unable to read version", - m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg()); - rollback(); - return SQLException::DATABASE_ERR; - } - m_hasVersionMismatch = !m_database->expectedVersion().isEmpty() && (m_database->expectedVersion() != actualVersion); - return 0; -} - -ExceptionCode SQLTransactionBackendSync::execute() -{ - ASSERT(m_database->scriptExecutionContext()->isContextThread()); - if (!m_database->opened() || (m_callback && !m_callback->handleEvent(SQLTransactionSync::from(this)))) { - if (m_database->lastErrorMessage().isEmpty()) - m_database->setLastErrorMessage("failed to execute transaction callback"); - m_callback = 0; - return SQLException::UNKNOWN_ERR; - } - - m_callback = 0; - return 0; -} - -ExceptionCode SQLTransactionBackendSync::commit() -{ - ASSERT(m_database->scriptExecutionContext()->isContextThread()); - if (!m_database->opened()) { - m_database->setLastErrorMessage("unable to commit transaction because the database is not open."); - return SQLException::UNKNOWN_ERR; - } - - ASSERT(m_sqliteTransaction); - - m_database->disableAuthorizer(); - m_sqliteTransaction->commit(); - m_database->enableAuthorizer(); - - // If the commit failed, the transaction will still be marked as "in progress" - if (m_sqliteTransaction->inProgress()) { - m_database->setLastErrorMessage("unable to commit transaction", - m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg()); - return SQLException::DATABASE_ERR; - } - - m_sqliteTransaction.clear(); - - // Vacuum the database if anything was deleted. - if (m_database->hadDeletes()) - m_database->incrementalVacuumIfNeeded(); - - // The commit was successful. If the transaction modified this database, notify the delegates. - if (m_modifiedDatabase) - m_transactionClient->didCommitWriteTransaction(database()); - return 0; -} - -void SQLTransactionBackendSync::rollback() -{ - ASSERT(m_database->scriptExecutionContext()->isContextThread()); - m_database->disableAuthorizer(); - if (m_sqliteTransaction) { - m_sqliteTransaction->rollback(); - m_sqliteTransaction.clear(); - } - m_database->enableAuthorizer(); - - ASSERT(!m_database->sqliteDatabase().transactionInProgress()); -} - -} // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionBackendSync.h b/Source/WebCore/Modules/webdatabase/SQLTransactionBackendSync.h deleted file mode 100644 index 73ab48de0..000000000 --- a/Source/WebCore/Modules/webdatabase/SQLTransactionBackendSync.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * 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. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR 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. - */ - -#ifndef SQLTransactionBackendSync_h -#define SQLTransactionBackendSync_h - -#if ENABLE(SQL_DATABASE) - -#include "DatabaseBasicTypes.h" -#include <wtf/Forward.h> -#include <wtf/RefCounted.h> -#include <wtf/Vector.h> -#include <wtf/text/WTFString.h> - -namespace WebCore { - -class DatabaseSync; -class SQLResultSet; -class SQLTransactionClient; -class SQLTransactionSyncCallback; -class SQLValue; -class SQLiteTransaction; - -// Instances of this class should be created and used only on the worker's context thread. -class SQLTransactionBackendSync : public RefCounted<SQLTransactionBackendSync> { -public: - ~SQLTransactionBackendSync(); - - PassRefPtr<SQLResultSet> executeSQL(const String& sqlStatement, const Vector<SQLValue>& arguments, ExceptionCode&); - - DatabaseSync* database() { return m_database.get(); } - bool isReadOnly() const { return m_readOnly; } - - ExceptionCode begin(); - ExceptionCode execute(); - ExceptionCode commit(); - void rollback(); - -private: - SQLTransactionBackendSync(DatabaseSync*, PassRefPtr<SQLTransactionSyncCallback>, bool readOnly); - - RefPtr<DatabaseSync> m_database; - RefPtr<SQLTransactionSyncCallback> m_callback; - bool m_readOnly; - bool m_hasVersionMismatch; - - bool m_modifiedDatabase; - OwnPtr<SQLTransactionClient> m_transactionClient; - OwnPtr<SQLiteTransaction> m_sqliteTransaction; - - friend class SQLTransactionSync; // FIXME: Remove this once the front-end has been properly isolated. -}; - -} // namespace WebCore - -#endif - -#endif // SQLTransactionBackendSync_h diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionCallback.h b/Source/WebCore/Modules/webdatabase/SQLTransactionCallback.h index 208ce111b..52cbe69e5 100644 --- a/Source/WebCore/Modules/webdatabase/SQLTransactionCallback.h +++ b/Source/WebCore/Modules/webdatabase/SQLTransactionCallback.h @@ -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. * @@ -26,10 +26,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SQLTransactionCallback_h -#define SQLTransactionCallback_h - -#if ENABLE(SQL_DATABASE) +#pragma once #include <wtf/ThreadSafeRefCounted.h> @@ -43,8 +40,4 @@ public: virtual bool handleEvent(SQLTransaction*) = 0; }; -} - -#endif - -#endif // SQLTransactionCallback_h +} // namespace WebCore diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionCallback.idl b/Source/WebCore/Modules/webdatabase/SQLTransactionCallback.idl index 0c4c32ae0..24f9fa8e7 100644 --- a/Source/WebCore/Modules/webdatabase/SQLTransactionCallback.idl +++ b/Source/WebCore/Modules/webdatabase/SQLTransactionCallback.idl @@ -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. * @@ -26,8 +26,4 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -[ - Conditional=SQL_DATABASE, -] callback interface SQLTransactionCallback { - boolean handleEvent(SQLTransaction transaction); -}; +callback SQLTransactionCallback = void (SQLTransaction transaction); diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionClient.cpp b/Source/WebCore/Modules/webdatabase/SQLTransactionClient.cpp deleted file mode 100644 index 938153eec..000000000 --- a/Source/WebCore/Modules/webdatabase/SQLTransactionClient.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2009 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * 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. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR 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. - */ - -#include "config.h" -#include "SQLTransactionClient.h" - -#if ENABLE(SQL_DATABASE) - -#include "DatabaseBackendBase.h" -#include "DatabaseBackendContext.h" -#include "DatabaseManager.h" -#include "DatabaseTracker.h" -#include "ScriptExecutionContext.h" -#include "SecurityOrigin.h" - -namespace WebCore { - -void SQLTransactionClient::didCommitWriteTransaction(DatabaseBackendBase* database) -{ - DatabaseTracker::tracker().scheduleNotifyDatabaseChanged( - database->securityOrigin(), database->stringIdentifier()); -} - -bool SQLTransactionClient::didExceedQuota(DatabaseBackendBase* database) -{ - ASSERT(database->databaseContext()->scriptExecutionContext()->isContextThread()); - unsigned long long currentQuota = DatabaseManager::manager().quotaForOrigin(database->securityOrigin()); - database->databaseContext()->databaseExceededQuota(database->stringIdentifier(), database->details()); - unsigned long long newQuota = DatabaseManager::manager().quotaForOrigin(database->securityOrigin()); - return (newQuota > currentQuota); -} - -} - -#endif // ENABLE(SQL_DATABASE) diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionClient.h b/Source/WebCore/Modules/webdatabase/SQLTransactionClient.h deleted file mode 100644 index cd65eafa6..000000000 --- a/Source/WebCore/Modules/webdatabase/SQLTransactionClient.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2009 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * 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. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR 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. - */ - -#ifndef SQLTransactionClient_h -#define SQLTransactionClient_h - -#if ENABLE(SQL_DATABASE) - -#include <wtf/FastMalloc.h> -#include <wtf/Noncopyable.h> - -namespace WebCore { - -class DatabaseBackendBase; - -// A client to the SQLTransaction class. Allows SQLTransaction to notify interested -// parties that certain things have happened in a transaction. -class SQLTransactionClient { - WTF_MAKE_NONCOPYABLE(SQLTransactionClient); WTF_MAKE_FAST_ALLOCATED; -public: - SQLTransactionClient() { } - void didCommitWriteTransaction(DatabaseBackendBase*); - bool didExceedQuota(DatabaseBackendBase*); -}; - -} - -#endif // ENABLE(SQL_DATABASE) - -#endif // SQLTransactionClient_h diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionCoordinator.cpp b/Source/WebCore/Modules/webdatabase/SQLTransactionCoordinator.cpp index bbb45a810..3aed24b0c 100644 --- a/Source/WebCore/Modules/webdatabase/SQLTransactionCoordinator.cpp +++ b/Source/WebCore/Modules/webdatabase/SQLTransactionCoordinator.cpp @@ -32,23 +32,18 @@ #include "config.h" #include "SQLTransactionCoordinator.h" -#if ENABLE(SQL_DATABASE) - -#include "DatabaseBackend.h" -#include "SQLTransactionBackend.h" +#include "Database.h" +#include "SQLTransaction.h" #include "SecurityOrigin.h" +#include "SecurityOriginData.h" #include <wtf/Deque.h> -#include <wtf/HashMap.h> -#include <wtf/HashSet.h> #include <wtf/RefPtr.h> namespace WebCore { -static String getDatabaseIdentifier(SQLTransactionBackend* transaction) +static String getDatabaseIdentifier(SQLTransaction& transaction) { - DatabaseBackend* database = transaction->database(); - ASSERT(database); - return database->securityOrigin()->databaseIdentifier(); + return transaction.database().securityOrigin().databaseIdentifier(); } SQLTransactionCoordinator::SQLTransactionCoordinator() @@ -61,7 +56,7 @@ void SQLTransactionCoordinator::processPendingTransactions(CoordinationInfo& inf if (info.activeWriteTransaction || info.pendingTransactions.isEmpty()) return; - RefPtr<SQLTransactionBackend> firstPendingTransaction = info.pendingTransactions.first(); + RefPtr<SQLTransaction> firstPendingTransaction = info.pendingTransactions.first(); if (firstPendingTransaction->isReadOnly()) { do { firstPendingTransaction = info.pendingTransactions.takeFirst(); @@ -75,7 +70,7 @@ void SQLTransactionCoordinator::processPendingTransactions(CoordinationInfo& inf } } -void SQLTransactionCoordinator::acquireLock(SQLTransactionBackend* transaction) +void SQLTransactionCoordinator::acquireLock(SQLTransaction& transaction) { ASSERT(!m_isShuttingDown); @@ -88,11 +83,11 @@ void SQLTransactionCoordinator::acquireLock(SQLTransactionBackend* transaction) } CoordinationInfo& info = coordinationInfoIterator->value; - info.pendingTransactions.append(transaction); + info.pendingTransactions.append(&transaction); processPendingTransactions(info); } -void SQLTransactionCoordinator::releaseLock(SQLTransactionBackend* transaction) +void SQLTransactionCoordinator::releaseLock(SQLTransaction& transaction) { if (m_isShuttingDown) return; @@ -103,12 +98,12 @@ void SQLTransactionCoordinator::releaseLock(SQLTransactionBackend* transaction) ASSERT(coordinationInfoIterator != m_coordinationInfoMap.end()); CoordinationInfo& info = coordinationInfoIterator->value; - if (transaction->isReadOnly()) { - ASSERT(info.activeReadTransactions.contains(transaction)); - info.activeReadTransactions.remove(transaction); + if (transaction.isReadOnly()) { + ASSERT(info.activeReadTransactions.contains(&transaction)); + info.activeReadTransactions.remove(&transaction); } else { - ASSERT(info.activeWriteTransaction == transaction); - info.activeWriteTransaction = 0; + ASSERT(info.activeWriteTransaction == &transaction); + info.activeWriteTransaction = nullptr; } processPendingTransactions(info); @@ -121,27 +116,20 @@ void SQLTransactionCoordinator::shutdown() m_isShuttingDown = true; // Notify all transactions in progress that the database thread is shutting down - for (CoordinationInfoMap::iterator coordinationInfoIterator = m_coordinationInfoMap.begin(); - coordinationInfoIterator != m_coordinationInfoMap.end(); ++coordinationInfoIterator) { - CoordinationInfo& info = coordinationInfoIterator->value; - + for (auto& info : m_coordinationInfoMap.values()) { // Clean up transactions that have reached "lockAcquired": // Transaction phase 4 cleanup. See comment on "What happens if a // transaction is interrupted?" at the top of SQLTransactionBackend.cpp. if (info.activeWriteTransaction) info.activeWriteTransaction->notifyDatabaseThreadIsShuttingDown(); - for (HashSet<RefPtr<SQLTransactionBackend>>::iterator activeReadTransactionsIterator = - info.activeReadTransactions.begin(); - activeReadTransactionsIterator != info.activeReadTransactions.end(); - ++activeReadTransactionsIterator) { - (*activeReadTransactionsIterator)->notifyDatabaseThreadIsShuttingDown(); - } + for (auto& transaction : info.activeReadTransactions) + transaction->notifyDatabaseThreadIsShuttingDown(); // Clean up transactions that have NOT reached "lockAcquired": // Transaction phase 3 cleanup. See comment on "What happens if a // transaction is interrupted?" at the top of SQLTransactionBackend.cpp. while (!info.pendingTransactions.isEmpty()) { - RefPtr<SQLTransactionBackend> transaction = info.pendingTransactions.first(); + RefPtr<SQLTransaction> transaction = info.pendingTransactions.first(); transaction->notifyDatabaseThreadIsShuttingDown(); } } @@ -151,5 +139,3 @@ void SQLTransactionCoordinator::shutdown() } } // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionCoordinator.h b/Source/WebCore/Modules/webdatabase/SQLTransactionCoordinator.h index ce87e055e..f9c89d812 100644 --- a/Source/WebCore/Modules/webdatabase/SQLTransactionCoordinator.h +++ b/Source/WebCore/Modules/webdatabase/SQLTransactionCoordinator.h @@ -29,10 +29,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SQLTransactionCoordinator_h -#define SQLTransactionCoordinator_h - -#if ENABLE(SQL_DATABASE) +#pragma once #include <wtf/Deque.h> #include <wtf/HashMap.h> @@ -42,21 +39,21 @@ namespace WebCore { -class SQLTransactionBackend; +class SQLTransaction; class SQLTransactionCoordinator { WTF_MAKE_NONCOPYABLE(SQLTransactionCoordinator); WTF_MAKE_FAST_ALLOCATED; public: SQLTransactionCoordinator(); - void acquireLock(SQLTransactionBackend*); - void releaseLock(SQLTransactionBackend*); + void acquireLock(SQLTransaction&); + void releaseLock(SQLTransaction&); void shutdown(); private: - typedef Deque<RefPtr<SQLTransactionBackend>> TransactionsQueue; + typedef Deque<RefPtr<SQLTransaction>> TransactionsQueue; struct CoordinationInfo { TransactionsQueue pendingTransactions; - HashSet<RefPtr<SQLTransactionBackend>> activeReadTransactions; - RefPtr<SQLTransactionBackend> activeWriteTransaction; + HashSet<RefPtr<SQLTransaction>> activeReadTransactions; + RefPtr<SQLTransaction> activeWriteTransaction; }; // Maps database names to information about pending transactions typedef HashMap<String, CoordinationInfo> CoordinationInfoMap; @@ -67,7 +64,3 @@ private: }; } // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) - -#endif // SQLTransactionCoordinator_h diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionErrorCallback.h b/Source/WebCore/Modules/webdatabase/SQLTransactionErrorCallback.h index 56727cc05..89f207ebe 100644 --- a/Source/WebCore/Modules/webdatabase/SQLTransactionErrorCallback.h +++ b/Source/WebCore/Modules/webdatabase/SQLTransactionErrorCallback.h @@ -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. * @@ -26,10 +26,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SQLTransactionErrorCallback_h -#define SQLTransactionErrorCallback_h - -#if ENABLE(SQL_DATABASE) +#pragma once #include <wtf/ThreadSafeRefCounted.h> @@ -43,8 +40,4 @@ public: virtual bool handleEvent(SQLError*) = 0; }; -} - -#endif - -#endif // SQLTransactionErrorCallback_h +} // namespace WebCore diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionErrorCallback.idl b/Source/WebCore/Modules/webdatabase/SQLTransactionErrorCallback.idl index bcf3b8184..450a46071 100644 --- a/Source/WebCore/Modules/webdatabase/SQLTransactionErrorCallback.idl +++ b/Source/WebCore/Modules/webdatabase/SQLTransactionErrorCallback.idl @@ -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. * @@ -26,8 +26,4 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -[ - Conditional=SQL_DATABASE, -] callback interface SQLTransactionErrorCallback { - boolean handleEvent(SQLError error); -}; +callback SQLTransactionErrorCallback = void (SQLError error); diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionState.h b/Source/WebCore/Modules/webdatabase/SQLTransactionState.h index f8a5adf62..eedf28f1b 100644 --- a/Source/WebCore/Modules/webdatabase/SQLTransactionState.h +++ b/Source/WebCore/Modules/webdatabase/SQLTransactionState.h @@ -23,10 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SQLTransactionState_h -#define SQLTransactionState_h - -#if ENABLE(SQL_DATABASE) +#pragma once namespace WebCore { @@ -48,7 +45,3 @@ enum class SQLTransactionState { }; } // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) - -#endif // SQLTransactionState_h diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionStateMachine.cpp b/Source/WebCore/Modules/webdatabase/SQLTransactionStateMachine.cpp index 194f862b4..ccd894033 100644 --- a/Source/WebCore/Modules/webdatabase/SQLTransactionStateMachine.cpp +++ b/Source/WebCore/Modules/webdatabase/SQLTransactionStateMachine.cpp @@ -26,8 +26,6 @@ #include "config.h" #include "SQLTransactionStateMachine.h" -#if ENABLE(SQL_DATABASE) - #include "Logging.h" #include <wtf/Assertions.h> @@ -70,5 +68,3 @@ const char* nameForSQLTransactionState(SQLTransactionState state) #endif } // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionStateMachine.h b/Source/WebCore/Modules/webdatabase/SQLTransactionStateMachine.h index fff11a8ce..b30fa28d8 100644 --- a/Source/WebCore/Modules/webdatabase/SQLTransactionStateMachine.h +++ b/Source/WebCore/Modules/webdatabase/SQLTransactionStateMachine.h @@ -23,10 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SQLTransactionStateMachine_h -#define SQLTransactionStateMachine_h - -#if ENABLE(SQL_DATABASE) +#pragma once #include "SQLTransactionState.h" #include <wtf/ThreadSafeRefCounted.h> @@ -41,7 +38,7 @@ public: protected: SQLTransactionStateMachine(); - typedef SQLTransactionState (T::* StateFunction)(); + typedef void (T::*StateFunction)(); virtual StateFunction stateFunctionFor(SQLTransactionState) = 0; void setStateToRequestedState(); @@ -92,21 +89,22 @@ template<typename T> void SQLTransactionStateMachine<T>::runStateMachine() { ASSERT(SQLTransactionState::End < SQLTransactionState::Idle); - while (m_nextState > SQLTransactionState::Idle) { - ASSERT(m_nextState < SQLTransactionState::NumberOfStates); - StateFunction stateFunction = stateFunctionFor(m_nextState); - ASSERT(stateFunction); + + if (m_nextState <= SQLTransactionState::Idle) + return; + + ASSERT(m_nextState < SQLTransactionState::NumberOfStates); + + StateFunction stateFunction = stateFunctionFor(m_nextState); + ASSERT(stateFunction); #ifndef NDEBUG - m_stateAuditTrail[m_nextStateAuditEntry] = m_nextState; - m_nextStateAuditEntry = (m_nextStateAuditEntry + 1) % s_sizeOfStateAuditTrail; + m_stateAuditTrail[m_nextStateAuditEntry] = m_nextState; + m_nextStateAuditEntry = (m_nextStateAuditEntry + 1) % s_sizeOfStateAuditTrail; #endif - m_nextState = (static_cast<T*>(this)->*stateFunction)(); - } + + (static_cast<T*>(this)->*stateFunction)(); + m_nextState = SQLTransactionState::Idle; } } // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) - -#endif // SQLTransactionStateMachine_h diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionSync.cpp b/Source/WebCore/Modules/webdatabase/SQLTransactionSync.cpp deleted file mode 100644 index 6443c9a05..000000000 --- a/Source/WebCore/Modules/webdatabase/SQLTransactionSync.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * 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. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR 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. - */ - -#include "config.h" -#include "SQLTransactionSync.h" - -#if ENABLE(SQL_DATABASE) - -#include "DatabaseSync.h" -#include "SQLTransactionSyncCallback.h" -#include "ScriptExecutionContext.h" - -namespace WebCore { - -PassRefPtr<SQLTransactionSync> SQLTransactionSync::create(DatabaseSync* db, PassRefPtr<SQLTransactionSyncCallback> callback, bool readOnly) -{ - return adoptRef(new SQLTransactionSync(db, callback, readOnly)); -} - -SQLTransactionSync::SQLTransactionSync(DatabaseSync* db, PassRefPtr<SQLTransactionSyncCallback> callback, bool readOnly) - : SQLTransactionBackendSync(db, callback, readOnly) -{ - ASSERT(m_database->scriptExecutionContext()->isContextThread()); -} - -SQLTransactionSync* SQLTransactionSync::from(SQLTransactionBackendSync* backend) -{ - return static_cast<SQLTransactionSync*>(backend); -} - -} // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionSync.h b/Source/WebCore/Modules/webdatabase/SQLTransactionSync.h deleted file mode 100644 index 78473f851..000000000 --- a/Source/WebCore/Modules/webdatabase/SQLTransactionSync.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * 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. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR 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. - */ - -#ifndef SQLTransactionSync_h -#define SQLTransactionSync_h - -#if ENABLE(SQL_DATABASE) - -#include "SQLTransactionBackendSync.h" - -namespace WebCore { - -// Instances of this class should be created and used only on the worker's context thread. -class SQLTransactionSync : public SQLTransactionBackendSync { -public: - static PassRefPtr<SQLTransactionSync> create(DatabaseSync*, PassRefPtr<SQLTransactionSyncCallback>, bool readOnly = false); - - static SQLTransactionSync* from(SQLTransactionBackendSync*); - -private: - SQLTransactionSync(DatabaseSync*, PassRefPtr<SQLTransactionSyncCallback>, bool readOnly); -}; - -} // namespace WebCore - -#endif - -#endif // SQLTransactionSync_h diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionSync.idl b/Source/WebCore/Modules/webdatabase/SQLTransactionSync.idl deleted file mode 100644 index 9b2a4f5a0..000000000 --- a/Source/WebCore/Modules/webdatabase/SQLTransactionSync.idl +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * 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. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR 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. - */ - -[ - NoInterfaceObject, - Conditional=SQL_DATABASE, - JSNoStaticTables, - ImplementationLacksVTable, -] interface SQLTransactionSync { - [Custom] SQLResultSet executeSql(DOMString sqlStatement, ObjectArray arguments); -}; diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionSyncCallback.h b/Source/WebCore/Modules/webdatabase/SQLTransactionSyncCallback.h deleted file mode 100644 index 0c5f8fd63..000000000 --- a/Source/WebCore/Modules/webdatabase/SQLTransactionSyncCallback.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * 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. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR 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. - */ - -#ifndef SQLTransactionSyncCallback_h -#define SQLTransactionSyncCallback_h - -#if ENABLE(SQL_DATABASE) - -#include <wtf/RefCounted.h> - -namespace WebCore { - -class SQLTransactionSync; - -// Instances of this class should be created and used only on the worker's context thread. -class SQLTransactionSyncCallback : public RefCounted<SQLTransactionSyncCallback> { -public: - virtual ~SQLTransactionSyncCallback() { } - virtual bool handleEvent(SQLTransactionSync*) = 0; -}; - -} - -#endif - -#endif // SQLTransactionSyncCallback_h diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionSyncCallback.idl b/Source/WebCore/Modules/webdatabase/SQLTransactionSyncCallback.idl deleted file mode 100644 index c5b5e1ed5..000000000 --- a/Source/WebCore/Modules/webdatabase/SQLTransactionSyncCallback.idl +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * 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. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR 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. - */ - -[ - Conditional=SQL_DATABASE, -] callback interface SQLTransactionSyncCallback { - boolean handleEvent(SQLTransactionSync transaction); -}; diff --git a/Source/WebCore/Modules/webdatabase/WorkerGlobalScopeWebDatabase.cpp b/Source/WebCore/Modules/webdatabase/WorkerGlobalScopeWebDatabase.cpp deleted file mode 100644 index d30ae8191..000000000 --- a/Source/WebCore/Modules/webdatabase/WorkerGlobalScopeWebDatabase.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. - * Copyright (C) 2009, 2011 Google Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 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. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * 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. - * - */ - -#include "config.h" - -#if ENABLE(SQL_DATABASE) - -#include "WorkerGlobalScopeWebDatabase.h" - -#include "Database.h" -#include "DatabaseCallback.h" -#include "DatabaseManager.h" -#include "DatabaseSync.h" -#include "SecurityOrigin.h" -#include "WorkerGlobalScope.h" - -namespace WebCore { - -PassRefPtr<Database> WorkerGlobalScopeWebDatabase::openDatabase(WorkerGlobalScope* context, const String& name, const String& version, const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback, ExceptionCode& ec) -{ - DatabaseManager& dbManager = DatabaseManager::manager(); - RefPtr<Database> database; - DatabaseError error = DatabaseError::None; - if (dbManager.isAvailable() && context->securityOrigin()->canAccessDatabase(context->topOrigin())) { - database = dbManager.openDatabase(context, name, version, displayName, estimatedSize, creationCallback, error); - ASSERT(database || error != DatabaseError::None); - ec = DatabaseManager::exceptionCodeForDatabaseError(error); - } else - ec = SECURITY_ERR; - - return database.release(); -} - -PassRefPtr<DatabaseSync> WorkerGlobalScopeWebDatabase::openDatabaseSync(WorkerGlobalScope* context, const String& name, const String& version, const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback, ExceptionCode& ec) -{ - DatabaseManager& dbManager = DatabaseManager::manager(); - RefPtr<DatabaseSync> database; - DatabaseError error = DatabaseError::None; - if (dbManager.isAvailable() && context->securityOrigin()->canAccessDatabase(context->topOrigin())) { - database = dbManager.openDatabaseSync(context, name, version, displayName, estimatedSize, creationCallback, error); - - ASSERT(database || error != DatabaseError::None); - ec = DatabaseManager::exceptionCodeForDatabaseError(error); - } else - ec = SECURITY_ERR; - - return database.release(); -} - -} // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) diff --git a/Source/WebCore/Modules/webdatabase/WorkerGlobalScopeWebDatabase.h b/Source/WebCore/Modules/webdatabase/WorkerGlobalScopeWebDatabase.h deleted file mode 100644 index fcccab1e3..000000000 --- a/Source/WebCore/Modules/webdatabase/WorkerGlobalScopeWebDatabase.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 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. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * 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. - * - */ - -#ifndef WorkerGlobalScopeWebDatabase_h -#define WorkerGlobalScopeWebDatabase_h - -#if ENABLE(SQL_DATABASE) - -#include "ExceptionCode.h" -#include <wtf/Forward.h> -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> -#include <wtf/RefPtr.h> - -namespace WebCore { - -class Database; -class DatabaseCallback; -class DatabaseSync; -class WorkerGlobalScope; - -class WorkerGlobalScopeWebDatabase { -public: - static PassRefPtr<Database> openDatabase(WorkerGlobalScope*, const String& name, const String& version, const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback, ExceptionCode&); - static PassRefPtr<DatabaseSync> openDatabaseSync(WorkerGlobalScope*, const String& name, const String& version, const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback, ExceptionCode&); - -private: - WorkerGlobalScopeWebDatabase() { }; - ~WorkerGlobalScopeWebDatabase() { }; -}; - -} // namespace WebCore - -#endif // ENABLE(SQL_DATABASE) - -#endif // WorkerGlobalScopeWebDatabase_h diff --git a/Source/WebCore/Modules/webdatabase/WorkerGlobalScopeWebDatabase.idl b/Source/WebCore/Modules/webdatabase/WorkerGlobalScopeWebDatabase.idl deleted file mode 100644 index d22d08765..000000000 --- a/Source/WebCore/Modules/webdatabase/WorkerGlobalScopeWebDatabase.idl +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 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. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * 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. - * - */ - -[ - Conditional=SQL_DATABASE, -] partial interface WorkerGlobalScope { - [RaisesException] Database openDatabase(DOMString name, DOMString version, DOMString displayName, unsigned long estimatedSize, optional DatabaseCallback creationCallback); - - [RaisesException] DatabaseSync openDatabaseSync(DOMString name, DOMString version, DOMString displayName, unsigned long estimatedSize, optional DatabaseCallback creationCallback); -}; - |