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:
create(PassRefPtr<Database> database,PassRefPtr<DatabaseCallback> creationCallback)67 static PassOwnPtr<DatabaseCreationCallbackTask> create(PassRefPtr<Database> database, PassRefPtr<DatabaseCallback> creationCallback)
68 {
69 return adoptPtr(new DatabaseCreationCallbackTask(database, creationCallback));
70 }
71
performTask(ScriptExecutionContext *)72 virtual void performTask(ScriptExecutionContext*)
73 {
74 m_creationCallback->handleEvent(m_database.get());
75 }
76
77 private:
DatabaseCreationCallbackTask(PassRefPtr<Database> database,PassRefPtr<DatabaseCallback> callback)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
openDatabase(ScriptExecutionContext * context,const String & name,const String & expectedVersion,const String & displayName,unsigned long estimatedSize,PassRefPtr<DatabaseCallback> creationCallback,ExceptionCode & e)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
Database(ScriptExecutionContext * context,const String & name,const String & expectedVersion,const String & displayName,unsigned long estimatedSize)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:
create(PassRefPtr<ScriptExecutionContext> context)139 static PassOwnPtr<DerefContextTask> create(PassRefPtr<ScriptExecutionContext> context)
140 {
141 return adoptPtr(new DerefContextTask(context));
142 }
143
performTask(ScriptExecutionContext * context)144 virtual void performTask(ScriptExecutionContext* context)
145 {
146 ASSERT_UNUSED(context, context == m_context);
147 m_context.clear();
148 }
149
isCleanupTask() const150 virtual bool isCleanupTask() const { return true; }
151
152 private:
DerefContextTask(PassRefPtr<ScriptExecutionContext> context)153 DerefContextTask(PassRefPtr<ScriptExecutionContext> context)
154 : m_context(context)
155 {
156 }
157
158 RefPtr<ScriptExecutionContext> m_context;
159 };
160
~Database()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
version() const173 String Database::version() const
174 {
175 if (m_deleted)
176 return String();
177 return AbstractDatabase::version();
178 }
179
openAndVerifyVersion(bool setVersionInNewDatabase,ExceptionCode & e)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
markAsDeletedAndClose()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
close()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
closeImmediately()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
maximumSize() const240 unsigned long long Database::maximumSize() const
241 {
242 return DatabaseTracker::tracker().getMaxSizeForDatabase(this);
243 }
244
performOpenAndVerify(bool setVersionInNewDatabase,ExceptionCode & e)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
changeVersion(const String & oldVersion,const String & newVersion,PassRefPtr<SQLTransactionCallback> callback,PassRefPtr<SQLTransactionErrorCallback> errorCallback,PassRefPtr<VoidCallback> successCallback)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
transaction(PassRefPtr<SQLTransactionCallback> callback,PassRefPtr<SQLTransactionErrorCallback> errorCallback,PassRefPtr<VoidCallback> successCallback)269 void Database::transaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback)
270 {
271 runTransaction(callback, errorCallback, successCallback, false);
272 }
273
readTransaction(PassRefPtr<SQLTransactionCallback> callback,PassRefPtr<SQLTransactionErrorCallback> errorCallback,PassRefPtr<VoidCallback> successCallback)274 void Database::readTransaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback)
275 {
276 runTransaction(callback, errorCallback, successCallback, true);
277 }
278
runTransaction(PassRefPtr<SQLTransactionCallback> callback,PassRefPtr<SQLTransactionErrorCallback> errorCallback,PassRefPtr<VoidCallback> successCallback,bool readOnly)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
inProgressTransactionCompleted()290 void Database::inProgressTransactionCompleted()
291 {
292 MutexLocker locker(m_transactionInProgressMutex);
293 m_transactionInProgress = false;
294 scheduleTransaction();
295 }
296
scheduleTransaction()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
scheduleTransactionStep(SQLTransaction * transaction,bool immediately)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:
create(PassRefPtr<SQLTransaction> transaction)330 static PassOwnPtr<DeliverPendingCallbackTask> create(PassRefPtr<SQLTransaction> transaction)
331 {
332 return adoptPtr(new DeliverPendingCallbackTask(transaction));
333 }
334
performTask(ScriptExecutionContext *)335 virtual void performTask(ScriptExecutionContext*)
336 {
337 m_transaction->performPendingCallback();
338 }
339
340 private:
DeliverPendingCallbackTask(PassRefPtr<SQLTransaction> transaction)341 DeliverPendingCallbackTask(PassRefPtr<SQLTransaction> transaction)
342 : m_transaction(transaction)
343 {
344 }
345
346 RefPtr<SQLTransaction> m_transaction;
347 };
348
scheduleTransactionCallback(SQLTransaction * transaction)349 void Database::scheduleTransactionCallback(SQLTransaction* transaction)
350 {
351 m_scriptExecutionContext->postTask(DeliverPendingCallbackTask::create(transaction));
352 }
353
performGetTableNames()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
transactionClient() const383 SQLTransactionClient* Database::transactionClient() const
384 {
385 return m_scriptExecutionContext->databaseThread()->transactionClient();
386 }
387
transactionCoordinator() const388 SQLTransactionCoordinator* Database::transactionCoordinator() const
389 {
390 return m_scriptExecutionContext->databaseThread()->transactionCoordinator();
391 }
392
tableNames()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
securityOrigin() const409 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