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