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 Computer, 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 "CrossThreadTask.h" 36#include "DatabaseBackendContext.h" 37#include "DatabaseCallback.h" 38#include "DatabaseContext.h" 39#include "DatabaseManager.h" 40#include "DatabaseTask.h" 41#include "DatabaseThread.h" 42#include "DatabaseTracker.h" 43#include "Document.h" 44#include "JSDOMWindow.h" 45#include "Logging.h" 46#include "NotImplemented.h" 47#include "Page.h" 48#include "SQLError.h" 49#include "SQLTransaction.h" 50#include "SQLTransactionCallback.h" 51#include "SQLTransactionErrorCallback.h" 52#include "SQLiteStatement.h" 53#include "ScriptExecutionContext.h" 54#include "SecurityOrigin.h" 55#include "VoidCallback.h" 56#include <wtf/OwnPtr.h> 57#include <wtf/PassOwnPtr.h> 58#include <wtf/PassRefPtr.h> 59#include <wtf/RefPtr.h> 60#include <wtf/StdLibExtras.h> 61#include <wtf/text/CString.h> 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 87class DerefContextTask : public ScriptExecutionContext::Task { 88public: 89 static PassOwnPtr<DerefContextTask> create(PassRefPtr<ScriptExecutionContext> context) 90 { 91 return adoptPtr(new DerefContextTask(context)); 92 } 93 94 virtual void performTask(ScriptExecutionContext* context) 95 { 96 ASSERT_UNUSED(context, context == m_context); 97 m_context.clear(); 98 } 99 100 virtual bool isCleanupTask() const { return true; } 101 102private: 103 DerefContextTask(PassRefPtr<ScriptExecutionContext> context) 104 : m_context(context) 105 { 106 } 107 108 RefPtr<ScriptExecutionContext> m_context; 109}; 110 111Database::~Database() 112{ 113 // 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. 114 if (!m_scriptExecutionContext->isContextThread()) { 115 // Grab a pointer to the script execution here because we're releasing it when we pass it to 116 // DerefContextTask::create. 117 ScriptExecutionContext* scriptExecutionContext = m_scriptExecutionContext.get(); 118 119 scriptExecutionContext->postTask(DerefContextTask::create(m_scriptExecutionContext.release())); 120 } 121} 122 123Database* Database::from(DatabaseBackend* backend) 124{ 125 return static_cast<Database*>(backend->m_frontend); 126} 127 128PassRefPtr<DatabaseBackend> Database::backend() 129{ 130 return this; 131} 132 133String Database::version() const 134{ 135 if (m_deleted) 136 return String(); 137 return DatabaseBackendBase::version(); 138} 139 140void Database::markAsDeletedAndClose() 141{ 142 if (m_deleted || !databaseContext()->databaseThread()) 143 return; 144 145 LOG(StorageAPI, "Marking %s (%p) as deleted", stringIdentifier().ascii().data(), this); 146 m_deleted = true; 147 148 DatabaseTaskSynchronizer synchronizer; 149 if (databaseContext()->databaseThread()->terminationRequested(&synchronizer)) { 150 LOG(StorageAPI, "Database handle %p is on a terminated DatabaseThread, cannot be marked for normal closure\n", this); 151 return; 152 } 153 154 OwnPtr<DatabaseCloseTask> task = DatabaseCloseTask::create(this, &synchronizer); 155 databaseContext()->databaseThread()->scheduleImmediateTask(task.release()); 156 synchronizer.waitForTaskCompletion(); 157} 158 159void Database::closeImmediately() 160{ 161 ASSERT(m_scriptExecutionContext->isContextThread()); 162 DatabaseThread* databaseThread = databaseContext()->databaseThread(); 163 if (databaseThread && !databaseThread->terminationRequested() && opened()) { 164 logErrorMessage("forcibly closing database"); 165 databaseThread->scheduleImmediateTask(DatabaseCloseTask::create(this, 0)); 166 } 167} 168 169void Database::changeVersion(const String& oldVersion, const String& newVersion, 170 PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, 171 PassRefPtr<VoidCallback> successCallback) 172{ 173 ChangeVersionData data(oldVersion, newVersion); 174 runTransaction(callback, errorCallback, successCallback, false, &data); 175} 176 177void Database::transaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback) 178{ 179 runTransaction(callback, errorCallback, successCallback, false); 180} 181 182void Database::readTransaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback) 183{ 184 runTransaction(callback, errorCallback, successCallback, true); 185} 186 187static void callTransactionErrorCallback(ScriptExecutionContext*, PassRefPtr<SQLTransactionErrorCallback> callback, PassRefPtr<SQLError> error) 188{ 189 callback->handleEvent(error.get()); 190} 191 192void Database::runTransaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, 193 PassRefPtr<VoidCallback> successCallback, bool readOnly, const ChangeVersionData* changeVersionData) 194{ 195 RefPtr<SQLTransactionErrorCallback> anotherRefToErrorCallback = errorCallback; 196 RefPtr<SQLTransaction> transaction = SQLTransaction::create(this, callback, successCallback, anotherRefToErrorCallback, readOnly); 197 198 RefPtr<SQLTransactionBackend> transactionBackend; 199 transactionBackend = backend()->runTransaction(transaction.release(), readOnly, changeVersionData); 200 if (!transactionBackend && anotherRefToErrorCallback) { 201 RefPtr<SQLError> error = SQLError::create(SQLError::UNKNOWN_ERR, "database has been closed"); 202 scriptExecutionContext()->postTask(createCallbackTask(&callTransactionErrorCallback, anotherRefToErrorCallback, error.release())); 203 } 204} 205 206class DeliverPendingCallbackTask : public ScriptExecutionContext::Task { 207public: 208 static PassOwnPtr<DeliverPendingCallbackTask> create(PassRefPtr<SQLTransaction> transaction) 209 { 210 return adoptPtr(new DeliverPendingCallbackTask(transaction)); 211 } 212 213 virtual void performTask(ScriptExecutionContext*) 214 { 215 m_transaction->performPendingCallback(); 216 } 217 218private: 219 DeliverPendingCallbackTask(PassRefPtr<SQLTransaction> transaction) 220 : m_transaction(transaction) 221 { 222 } 223 224 RefPtr<SQLTransaction> m_transaction; 225}; 226 227void Database::scheduleTransactionCallback(SQLTransaction* transaction) 228{ 229 m_scriptExecutionContext->postTask(DeliverPendingCallbackTask::create(transaction)); 230} 231 232Vector<String> Database::performGetTableNames() 233{ 234 disableAuthorizer(); 235 236 SQLiteStatement statement(sqliteDatabase(), "SELECT name FROM sqlite_master WHERE type='table';"); 237 if (statement.prepare() != SQLResultOk) { 238 LOG_ERROR("Unable to retrieve list of tables for database %s", databaseDebugName().ascii().data()); 239 enableAuthorizer(); 240 return Vector<String>(); 241 } 242 243 Vector<String> tableNames; 244 int result; 245 while ((result = statement.step()) == SQLResultRow) { 246 String name = statement.getColumnText(0); 247 if (name != databaseInfoTableName()) 248 tableNames.append(name); 249 } 250 251 enableAuthorizer(); 252 253 if (result != SQLResultDone) { 254 LOG_ERROR("Error getting tables for database %s", databaseDebugName().ascii().data()); 255 return Vector<String>(); 256 } 257 258 return tableNames; 259} 260 261Vector<String> Database::tableNames() 262{ 263 // FIXME: Not using isolatedCopy on these strings looks ok since threads take strict turns 264 // in dealing with them. However, if the code changes, this may not be true anymore. 265 Vector<String> result; 266 DatabaseTaskSynchronizer synchronizer; 267 if (!databaseContext()->databaseThread() || databaseContext()->databaseThread()->terminationRequested(&synchronizer)) 268 return result; 269 270 OwnPtr<DatabaseTableNamesTask> task = DatabaseTableNamesTask::create(this, &synchronizer, result); 271 databaseContext()->databaseThread()->scheduleImmediateTask(task.release()); 272 synchronizer.waitForTaskCompletion(); 273 274 return result; 275} 276 277SecurityOrigin* Database::securityOrigin() const 278{ 279 if (m_scriptExecutionContext->isContextThread()) 280 return m_contextThreadSecurityOrigin.get(); 281 if (currentThread() == databaseContext()->databaseThread()->getThreadID()) 282 return m_databaseThreadSecurityOrigin.get(); 283 return 0; 284} 285 286} // namespace WebCore 287 288#endif // ENABLE(SQL_DATABASE) 289