1/* 2 * Copyright (C) 2007, 2008, 2013 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of Apple Inc. ("Apple") nor the names of 14 * its contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include "config.h" 30#include "Database.h" 31 32#if ENABLE(SQL_DATABASE) 33 34#include "ChangeVersionData.h" 35#include "DatabaseBackendContext.h" 36#include "DatabaseCallback.h" 37#include "DatabaseContext.h" 38#include "DatabaseManager.h" 39#include "DatabaseTask.h" 40#include "DatabaseThread.h" 41#include "DatabaseTracker.h" 42#include "Document.h" 43#include "JSDOMWindow.h" 44#include "Logging.h" 45#include "Page.h" 46#include "SQLError.h" 47#include "SQLTransaction.h" 48#include "SQLTransactionCallback.h" 49#include "SQLTransactionErrorCallback.h" 50#include "SQLiteStatement.h" 51#include "ScriptExecutionContext.h" 52#include "SecurityOrigin.h" 53#include "VoidCallback.h" 54#include <wtf/PassRefPtr.h> 55#include <wtf/RefPtr.h> 56#include <wtf/StdLibExtras.h> 57#include <wtf/text/CString.h> 58 59#if PLATFORM(IOS) 60#include "SQLiteDatabaseTracker.h" 61#endif 62 63namespace WebCore { 64 65PassRefPtr<Database> Database::create(ScriptExecutionContext*, PassRefPtr<DatabaseBackendBase> backend) 66{ 67 // FIXME: Currently, we're only simulating the backend by return the 68 // frontend database as its own the backend. When we split the 2 apart, 69 // this create() function should be changed to be a factory method for 70 // instantiating the backend. 71 return static_cast<Database*>(backend.get()); 72} 73 74Database::Database(PassRefPtr<DatabaseBackendContext> databaseContext, 75 const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize) 76 : DatabaseBase(databaseContext->scriptExecutionContext()) 77 , DatabaseBackend(databaseContext, name, expectedVersion, displayName, estimatedSize) 78 , m_databaseContext(DatabaseBackend::databaseContext()->frontend()) 79 , m_deleted(false) 80{ 81 m_databaseThreadSecurityOrigin = m_contextThreadSecurityOrigin->isolatedCopy(); 82 setFrontend(this); 83 84 ASSERT(m_databaseContext->databaseThread()); 85} 86 87Database::~Database() 88{ 89 // 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. 90 if (!m_scriptExecutionContext->isContextThread()) { 91 // Grab a pointer to the script execution here because we're releasing it when we pass it to 92 // DerefContextTask::create. 93 PassRefPtr<ScriptExecutionContext> passedContext = m_scriptExecutionContext.release(); 94 passedContext->postTask({ScriptExecutionContext::Task::CleanupTask, [=] (ScriptExecutionContext& context) { 95 ASSERT_UNUSED(context, &context == passedContext); 96 RefPtr<ScriptExecutionContext> scriptExecutionContext(passedContext); 97 }}); 98 } 99} 100 101Database* Database::from(DatabaseBackend* backend) 102{ 103 return static_cast<Database*>(backend->m_frontend); 104} 105 106PassRefPtr<DatabaseBackend> Database::backend() 107{ 108 return this; 109} 110 111String Database::version() const 112{ 113 if (m_deleted) 114 return String(); 115 return DatabaseBackendBase::version(); 116} 117 118void Database::markAsDeletedAndClose() 119{ 120 if (m_deleted || !databaseContext()->databaseThread()) 121 return; 122 123 LOG(StorageAPI, "Marking %s (%p) as deleted", stringIdentifier().ascii().data(), this); 124 m_deleted = true; 125 126 DatabaseTaskSynchronizer synchronizer; 127 if (databaseContext()->databaseThread()->terminationRequested(&synchronizer)) { 128 LOG(StorageAPI, "Database handle %p is on a terminated DatabaseThread, cannot be marked for normal closure\n", this); 129 return; 130 } 131 132 auto task = DatabaseCloseTask::create(this, &synchronizer); 133 databaseContext()->databaseThread()->scheduleImmediateTask(WTF::move(task)); 134 synchronizer.waitForTaskCompletion(); 135} 136 137void Database::closeImmediately() 138{ 139 ASSERT(m_scriptExecutionContext->isContextThread()); 140 DatabaseThread* databaseThread = databaseContext()->databaseThread(); 141 if (databaseThread && !databaseThread->terminationRequested() && opened()) { 142 logErrorMessage("forcibly closing database"); 143 databaseThread->scheduleImmediateTask(DatabaseCloseTask::create(this, 0)); 144 } 145} 146 147void Database::changeVersion(const String& oldVersion, const String& newVersion, 148 PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, 149 PassRefPtr<VoidCallback> successCallback) 150{ 151 ChangeVersionData data(oldVersion, newVersion); 152 runTransaction(callback, errorCallback, successCallback, false, &data); 153} 154 155void Database::transaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback) 156{ 157 runTransaction(callback, errorCallback, successCallback, false); 158} 159 160void Database::readTransaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback) 161{ 162 runTransaction(callback, errorCallback, successCallback, true); 163} 164 165void Database::runTransaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, 166 PassRefPtr<VoidCallback> successCallback, bool readOnly, const ChangeVersionData* changeVersionData) 167{ 168 RefPtr<SQLTransactionErrorCallback> errorCallbackProtector(errorCallback); 169 RefPtr<SQLTransaction> transaction = SQLTransaction::create(this, callback, successCallback, errorCallbackProtector, readOnly); 170 171 RefPtr<SQLTransactionBackend> transactionBackend(backend()->runTransaction(transaction.release(), readOnly, changeVersionData)); 172 if (!transactionBackend && errorCallbackProtector) 173 scriptExecutionContext()->postTask([=] (ScriptExecutionContext&) { 174 errorCallbackProtector->handleEvent(SQLError::create(SQLError::UNKNOWN_ERR, "database has been closed").get()); 175 }); 176} 177 178void Database::scheduleTransactionCallback(SQLTransaction* transaction) 179{ 180 RefPtr<SQLTransaction> transactionProtector(transaction); 181 m_scriptExecutionContext->postTask([=] (ScriptExecutionContext&) { 182 transactionProtector->performPendingCallback(); 183 }); 184} 185 186Vector<String> Database::performGetTableNames() 187{ 188 disableAuthorizer(); 189 190 SQLiteStatement statement(sqliteDatabase(), "SELECT name FROM sqlite_master WHERE type='table';"); 191 if (statement.prepare() != SQLResultOk) { 192 LOG_ERROR("Unable to retrieve list of tables for database %s", databaseDebugName().ascii().data()); 193 enableAuthorizer(); 194 return Vector<String>(); 195 } 196 197 Vector<String> tableNames; 198 int result; 199 while ((result = statement.step()) == SQLResultRow) { 200 String name = statement.getColumnText(0); 201 if (name != databaseInfoTableName()) 202 tableNames.append(name); 203 } 204 205 enableAuthorizer(); 206 207 if (result != SQLResultDone) { 208 LOG_ERROR("Error getting tables for database %s", databaseDebugName().ascii().data()); 209 return Vector<String>(); 210 } 211 212 return tableNames; 213} 214 215Vector<String> Database::tableNames() 216{ 217 // FIXME: Not using isolatedCopy on these strings looks ok since threads take strict turns 218 // in dealing with them. However, if the code changes, this may not be true anymore. 219 Vector<String> result; 220 DatabaseTaskSynchronizer synchronizer; 221 if (!databaseContext()->databaseThread() || databaseContext()->databaseThread()->terminationRequested(&synchronizer)) 222 return result; 223 224 auto task = DatabaseTableNamesTask::create(this, &synchronizer, result); 225 databaseContext()->databaseThread()->scheduleImmediateTask(WTF::move(task)); 226 synchronizer.waitForTaskCompletion(); 227 228 return result; 229} 230 231SecurityOrigin* Database::securityOrigin() const 232{ 233 if (m_scriptExecutionContext->isContextThread()) 234 return m_contextThreadSecurityOrigin.get(); 235 if (currentThread() == databaseContext()->databaseThread()->getThreadID()) 236 return m_databaseThreadSecurityOrigin.get(); 237 return 0; 238} 239 240} // namespace WebCore 241 242#endif // ENABLE(SQL_DATABASE) 243