1 /* 2 * Copyright (C) 2007, 2008 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(DATABASE) 33 #include "ChangeVersionWrapper.h" 34 #include "DatabaseCallback.h" 35 #include "DatabaseTask.h" 36 #include "DatabaseThread.h" 37 #include "DatabaseTracker.h" 38 #include "Document.h" 39 #include "InspectorDatabaseInstrumentation.h" 40 #include "Logging.h" 41 #include "NotImplemented.h" 42 #include "Page.h" 43 #include "SQLTransactionCallback.h" 44 #include "SQLTransactionClient.h" 45 #include "SQLTransactionCoordinator.h" 46 #include "SQLTransactionErrorCallback.h" 47 #include "SQLiteStatement.h" 48 #include "ScriptController.h" 49 #include "ScriptExecutionContext.h" 50 #include "SecurityOrigin.h" 51 #include "VoidCallback.h" 52 #include <wtf/OwnPtr.h> 53 #include <wtf/PassOwnPtr.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 USE(JSC) 60 #include "JSDOMWindow.h" 61 #endif 62 63 namespace WebCore { 64 65 class DatabaseCreationCallbackTask : public ScriptExecutionContext::Task { 66 public: 67 static PassOwnPtr<DatabaseCreationCallbackTask> create(PassRefPtr<Database> database, PassRefPtr<DatabaseCallback> creationCallback) 68 { 69 return adoptPtr(new DatabaseCreationCallbackTask(database, creationCallback)); 70 } 71 72 virtual void performTask(ScriptExecutionContext*) 73 { 74 m_creationCallback->handleEvent(m_database.get()); 75 } 76 77 private: 78 DatabaseCreationCallbackTask(PassRefPtr<Database> database, PassRefPtr<DatabaseCallback> callback) 79 : m_database(database) 80 , m_creationCallback(callback) 81 { 82 } 83 84 RefPtr<Database> m_database; 85 RefPtr<DatabaseCallback> m_creationCallback; 86 }; 87 88 PassRefPtr<Database> Database::openDatabase(ScriptExecutionContext* context, const String& name, 89 const String& expectedVersion, const String& displayName, 90 unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback, 91 ExceptionCode& e) 92 { 93 if (!DatabaseTracker::tracker().canEstablishDatabase(context, name, displayName, estimatedSize)) { 94 LOG(StorageAPI, "Database %s for origin %s not allowed to be established", name.ascii().data(), context->securityOrigin()->toString().ascii().data()); 95 return 0; 96 } 97 98 RefPtr<Database> database = adoptRef(new Database(context, name, expectedVersion, displayName, estimatedSize)); 99 100 if (!database->openAndVerifyVersion(!creationCallback, e)) { 101 LOG(StorageAPI, "Failed to open and verify version (expected %s) of database %s", expectedVersion.ascii().data(), database->databaseDebugName().ascii().data()); 102 DatabaseTracker::tracker().removeOpenDatabase(database.get()); 103 return 0; 104 } 105 106 DatabaseTracker::tracker().setDatabaseDetails(context->securityOrigin(), name, displayName, estimatedSize); 107 108 context->setHasOpenDatabases(); 109 110 InspectorInstrumentation::didOpenDatabase(context, database, context->securityOrigin()->host(), name, expectedVersion); 111 112 // If it's a new database and a creation callback was provided, reset the expected 113 // version to "" and schedule the creation callback. Because of some subtle String 114 // implementation issues, we have to reset m_expectedVersion here instead of doing 115 // it inside performOpenAndVerify() which is run on the DB thread. 116 if (database->isNew() && creationCallback.get()) { 117 database->m_expectedVersion = ""; 118 LOG(StorageAPI, "Scheduling DatabaseCreationCallbackTask for database %p\n", database.get()); 119 database->m_scriptExecutionContext->postTask(DatabaseCreationCallbackTask::create(database, creationCallback)); 120 } 121 122 return database; 123 } 124 125 Database::Database(ScriptExecutionContext* context, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize) 126 : AbstractDatabase(context, name, expectedVersion, displayName, estimatedSize) 127 , m_transactionInProgress(false) 128 , m_isTransactionQueueEnabled(true) 129 , m_deleted(false) 130 { 131 m_databaseThreadSecurityOrigin = m_contextThreadSecurityOrigin->threadsafeCopy(); 132 133 ScriptController::initializeThreading(); 134 ASSERT(m_scriptExecutionContext->databaseThread()); 135 } 136 137 class DerefContextTask : public ScriptExecutionContext::Task { 138 public: 139 static PassOwnPtr<DerefContextTask> create(PassRefPtr<ScriptExecutionContext> context) 140 { 141 return adoptPtr(new DerefContextTask(context)); 142 } 143 144 virtual void performTask(ScriptExecutionContext* context) 145 { 146 ASSERT_UNUSED(context, context == m_context); 147 m_context.clear(); 148 } 149 150 virtual bool isCleanupTask() const { return true; } 151 152 private: 153 DerefContextTask(PassRefPtr<ScriptExecutionContext> context) 154 : m_context(context) 155 { 156 } 157 158 RefPtr<ScriptExecutionContext> m_context; 159 }; 160 161 Database::~Database() 162 { 163 // 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. 164 if (!m_scriptExecutionContext->isContextThread()) { 165 // Grab a pointer to the script execution here because we're releasing it when we pass it to 166 // DerefContextTask::create. 167 ScriptExecutionContext* scriptExecutionContext = m_scriptExecutionContext.get(); 168 169 scriptExecutionContext->postTask(DerefContextTask::create(m_scriptExecutionContext.release())); 170 } 171 } 172 173 String Database::version() const 174 { 175 if (m_deleted) 176 return String(); 177 return AbstractDatabase::version(); 178 } 179 180 bool Database::openAndVerifyVersion(bool setVersionInNewDatabase, ExceptionCode& e) 181 { 182 DatabaseTaskSynchronizer synchronizer; 183 if (!m_scriptExecutionContext->databaseThread() || m_scriptExecutionContext->databaseThread()->terminationRequested(&synchronizer)) 184 return false; 185 186 bool success = false; 187 OwnPtr<DatabaseOpenTask> task = DatabaseOpenTask::create(this, setVersionInNewDatabase, &synchronizer, e, success); 188 m_scriptExecutionContext->databaseThread()->scheduleImmediateTask(task.release()); 189 synchronizer.waitForTaskCompletion(); 190 191 return success; 192 } 193 194 void Database::markAsDeletedAndClose() 195 { 196 if (m_deleted || !m_scriptExecutionContext->databaseThread()) 197 return; 198 199 LOG(StorageAPI, "Marking %s (%p) as deleted", stringIdentifier().ascii().data(), this); 200 m_deleted = true; 201 202 DatabaseTaskSynchronizer synchronizer; 203 if (m_scriptExecutionContext->databaseThread()->terminationRequested(&synchronizer)) { 204 LOG(StorageAPI, "Database handle %p is on a terminated DatabaseThread, cannot be marked for normal closure\n", this); 205 return; 206 } 207 208 OwnPtr<DatabaseCloseTask> task = DatabaseCloseTask::create(this, &synchronizer); 209 m_scriptExecutionContext->databaseThread()->scheduleImmediateTask(task.release()); 210 synchronizer.waitForTaskCompletion(); 211 } 212 213 void Database::close() 214 { 215 ASSERT(m_scriptExecutionContext->databaseThread()); 216 ASSERT(currentThread() == m_scriptExecutionContext->databaseThread()->getThreadID()); 217 218 { 219 MutexLocker locker(m_transactionInProgressMutex); 220 m_isTransactionQueueEnabled = false; 221 m_transactionInProgress = false; 222 } 223 224 closeDatabase(); 225 226 // Must ref() before calling databaseThread()->recordDatabaseClosed(). 227 RefPtr<Database> protect = this; 228 m_scriptExecutionContext->databaseThread()->recordDatabaseClosed(this); 229 m_scriptExecutionContext->databaseThread()->unscheduleDatabaseTasks(this); 230 DatabaseTracker::tracker().removeOpenDatabase(this); 231 } 232 233 void Database::closeImmediately() 234 { 235 DatabaseThread* databaseThread = scriptExecutionContext()->databaseThread(); 236 if (databaseThread && !databaseThread->terminationRequested() && opened()) 237 databaseThread->scheduleImmediateTask(DatabaseCloseTask::create(this, 0)); 238 } 239 240 unsigned long long Database::maximumSize() const 241 { 242 return DatabaseTracker::tracker().getMaxSizeForDatabase(this); 243 } 244 245 bool Database::performOpenAndVerify(bool setVersionInNewDatabase, ExceptionCode& e) 246 { 247 if (AbstractDatabase::performOpenAndVerify(setVersionInNewDatabase, e)) { 248 if (m_scriptExecutionContext->databaseThread()) 249 m_scriptExecutionContext->databaseThread()->recordDatabaseOpen(this); 250 251 return true; 252 } 253 254 return false; 255 } 256 257 void Database::changeVersion(const String& oldVersion, const String& newVersion, 258 PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, 259 PassRefPtr<VoidCallback> successCallback) 260 { 261 RefPtr<SQLTransaction> transaction = 262 SQLTransaction::create(this, callback, errorCallback, successCallback, ChangeVersionWrapper::create(oldVersion, newVersion)); 263 MutexLocker locker(m_transactionInProgressMutex); 264 m_transactionQueue.append(transaction.release()); 265 if (!m_transactionInProgress) 266 scheduleTransaction(); 267 } 268 269 void Database::transaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback) 270 { 271 runTransaction(callback, errorCallback, successCallback, false); 272 } 273 274 void Database::readTransaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback) 275 { 276 runTransaction(callback, errorCallback, successCallback, true); 277 } 278 279 void Database::runTransaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, 280 PassRefPtr<VoidCallback> successCallback, bool readOnly) 281 { 282 RefPtr<SQLTransaction> transaction = 283 SQLTransaction::create(this, callback, errorCallback, successCallback, 0, readOnly); 284 MutexLocker locker(m_transactionInProgressMutex); 285 m_transactionQueue.append(transaction.release()); 286 if (!m_transactionInProgress) 287 scheduleTransaction(); 288 } 289 290 void Database::inProgressTransactionCompleted() 291 { 292 MutexLocker locker(m_transactionInProgressMutex); 293 m_transactionInProgress = false; 294 scheduleTransaction(); 295 } 296 297 void Database::scheduleTransaction() 298 { 299 ASSERT(!m_transactionInProgressMutex.tryLock()); // Locked by caller. 300 RefPtr<SQLTransaction> transaction; 301 302 if (m_isTransactionQueueEnabled && !m_transactionQueue.isEmpty()) { 303 transaction = m_transactionQueue.takeFirst(); 304 } 305 306 if (transaction && m_scriptExecutionContext->databaseThread()) { 307 OwnPtr<DatabaseTransactionTask> task = DatabaseTransactionTask::create(transaction); 308 LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for transaction %p\n", task.get(), task->transaction()); 309 m_transactionInProgress = true; 310 m_scriptExecutionContext->databaseThread()->scheduleTask(task.release()); 311 } else 312 m_transactionInProgress = false; 313 } 314 315 void Database::scheduleTransactionStep(SQLTransaction* transaction, bool immediately) 316 { 317 if (!m_scriptExecutionContext->databaseThread()) 318 return; 319 320 OwnPtr<DatabaseTransactionTask> task = DatabaseTransactionTask::create(transaction); 321 LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for the transaction step\n", task.get()); 322 if (immediately) 323 m_scriptExecutionContext->databaseThread()->scheduleImmediateTask(task.release()); 324 else 325 m_scriptExecutionContext->databaseThread()->scheduleTask(task.release()); 326 } 327 328 class DeliverPendingCallbackTask : public ScriptExecutionContext::Task { 329 public: 330 static PassOwnPtr<DeliverPendingCallbackTask> create(PassRefPtr<SQLTransaction> transaction) 331 { 332 return adoptPtr(new DeliverPendingCallbackTask(transaction)); 333 } 334 335 virtual void performTask(ScriptExecutionContext*) 336 { 337 m_transaction->performPendingCallback(); 338 } 339 340 private: 341 DeliverPendingCallbackTask(PassRefPtr<SQLTransaction> transaction) 342 : m_transaction(transaction) 343 { 344 } 345 346 RefPtr<SQLTransaction> m_transaction; 347 }; 348 349 void Database::scheduleTransactionCallback(SQLTransaction* transaction) 350 { 351 m_scriptExecutionContext->postTask(DeliverPendingCallbackTask::create(transaction)); 352 } 353 354 Vector<String> Database::performGetTableNames() 355 { 356 disableAuthorizer(); 357 358 SQLiteStatement statement(sqliteDatabase(), "SELECT name FROM sqlite_master WHERE type='table';"); 359 if (statement.prepare() != SQLResultOk) { 360 LOG_ERROR("Unable to retrieve list of tables for database %s", databaseDebugName().ascii().data()); 361 enableAuthorizer(); 362 return Vector<String>(); 363 } 364 365 Vector<String> tableNames; 366 int result; 367 while ((result = statement.step()) == SQLResultRow) { 368 String name = statement.getColumnText(0); 369 if (name != databaseInfoTableName()) 370 tableNames.append(name); 371 } 372 373 enableAuthorizer(); 374 375 if (result != SQLResultDone) { 376 LOG_ERROR("Error getting tables for database %s", databaseDebugName().ascii().data()); 377 return Vector<String>(); 378 } 379 380 return tableNames; 381 } 382 383 SQLTransactionClient* Database::transactionClient() const 384 { 385 return m_scriptExecutionContext->databaseThread()->transactionClient(); 386 } 387 388 SQLTransactionCoordinator* Database::transactionCoordinator() const 389 { 390 return m_scriptExecutionContext->databaseThread()->transactionCoordinator(); 391 } 392 393 Vector<String> Database::tableNames() 394 { 395 // FIXME: Not using threadsafeCopy on these strings looks ok since threads take strict turns 396 // in dealing with them. However, if the code changes, this may not be true anymore. 397 Vector<String> result; 398 DatabaseTaskSynchronizer synchronizer; 399 if (!m_scriptExecutionContext->databaseThread() || m_scriptExecutionContext->databaseThread()->terminationRequested(&synchronizer)) 400 return result; 401 402 OwnPtr<DatabaseTableNamesTask> task = DatabaseTableNamesTask::create(this, &synchronizer, result); 403 m_scriptExecutionContext->databaseThread()->scheduleImmediateTask(task.release()); 404 synchronizer.waitForTaskCompletion(); 405 406 return result; 407 } 408 409 SecurityOrigin* Database::securityOrigin() const 410 { 411 if (m_scriptExecutionContext->isContextThread()) 412 return m_contextThreadSecurityOrigin.get(); 413 if (currentThread() == m_scriptExecutionContext->databaseThread()->getThreadID()) 414 return m_databaseThreadSecurityOrigin.get(); 415 return 0; 416 } 417 418 } // namespace WebCore 419 420 #endif // ENABLE(DATABASE) 421