d0c3a7a1e5bd786288b64329d1cbb08c71ac18ea
[WebKit-https.git] / Source / WebCore / Modules / webdatabase / Database.cpp
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(SQL_DATABASE)
33
34 #include "ChangeVersionWrapper.h"
35 #include "CrossThreadTask.h"
36 #include "DatabaseCallback.h"
37 #include "DatabaseContext.h"
38 #include "DatabaseTask.h"
39 #include "DatabaseThread.h"
40 #include "DatabaseTracker.h"
41 #include "Document.h"
42 #include "InspectorDatabaseInstrumentation.h"
43 #include "Logging.h"
44 #include "NotImplemented.h"
45 #include "Page.h"
46 #include "SQLError.h"
47 #include "SQLTransactionCallback.h"
48 #include "SQLTransactionClient.h"
49 #include "SQLTransactionCoordinator.h"
50 #include "SQLTransactionErrorCallback.h"
51 #include "SQLiteStatement.h"
52 #include "ScriptController.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
63 #if USE(JSC)
64 #include "JSDOMWindow.h"
65 #endif
66
67 namespace WebCore {
68
69 class DatabaseCreationCallbackTask : public ScriptExecutionContext::Task {
70 public:
71     static PassOwnPtr<DatabaseCreationCallbackTask> create(PassRefPtr<Database> database, PassRefPtr<DatabaseCallback> creationCallback)
72     {
73         return adoptPtr(new DatabaseCreationCallbackTask(database, creationCallback));
74     }
75
76     virtual void performTask(ScriptExecutionContext*)
77     {
78         m_creationCallback->handleEvent(m_database.get());
79     }
80
81 private:
82     DatabaseCreationCallbackTask(PassRefPtr<Database> database, PassRefPtr<DatabaseCallback> callback)
83         : m_database(database)
84         , m_creationCallback(callback)
85     {
86     }
87
88     RefPtr<Database> m_database;
89     RefPtr<DatabaseCallback> m_creationCallback;
90 };
91
92 PassRefPtr<Database> Database::openDatabase(ScriptExecutionContext* context, const String& name,
93                                             const String& expectedVersion, const String& displayName,
94                                             unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback,
95                                             ExceptionCode& e)
96 {
97     if (!DatabaseTracker::tracker().canEstablishDatabase(context, name, displayName, estimatedSize)) {
98         LOG(StorageAPI, "Database %s for origin %s not allowed to be established", name.ascii().data(), context->securityOrigin()->toString().ascii().data());
99         return 0;
100     }
101
102     RefPtr<Database> database = adoptRef(new Database(context, name, expectedVersion, displayName, estimatedSize));
103
104     String errorMessage;
105     if (!database->openAndVerifyVersion(!creationCallback, e, errorMessage)) {
106         database->logErrorMessage(errorMessage);
107         DatabaseTracker::tracker().removeOpenDatabase(database.get());
108         return 0;
109     }
110
111     DatabaseTracker::tracker().setDatabaseDetails(context->securityOrigin(), name, displayName, estimatedSize);
112
113     DatabaseContext::from(context)->setHasOpenDatabases();
114
115     InspectorInstrumentation::didOpenDatabase(context, database, context->securityOrigin()->host(), name, expectedVersion);
116
117     if (database->isNew() && creationCallback.get()) {
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, AsyncDatabase)
127     , m_transactionInProgress(false)
128     , m_isTransactionQueueEnabled(true)
129     , m_deleted(false)
130 {
131     m_databaseThreadSecurityOrigin = m_contextThreadSecurityOrigin->isolatedCopy();
132
133     ScriptController::initializeThreading();
134     ASSERT(databaseContext()->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, String& errorMessage)
181 {
182     DatabaseTaskSynchronizer synchronizer;
183     if (!databaseContext()->databaseThread() || databaseContext()->databaseThread()->terminationRequested(&synchronizer))
184         return false;
185
186     bool success = false;
187     OwnPtr<DatabaseOpenTask> task = DatabaseOpenTask::create(this, setVersionInNewDatabase, &synchronizer, e, errorMessage, success);
188     databaseContext()->databaseThread()->scheduleImmediateTask(task.release());
189     synchronizer.waitForTaskCompletion();
190
191     return success;
192 }
193
194 void Database::markAsDeletedAndClose()
195 {
196     if (m_deleted || !databaseContext()->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 (databaseContext()->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     databaseContext()->databaseThread()->scheduleImmediateTask(task.release());
210     synchronizer.waitForTaskCompletion();
211 }
212
213 void Database::close()
214 {
215     ASSERT(databaseContext()->databaseThread());
216     ASSERT(currentThread() == databaseContext()->databaseThread()->getThreadID());
217
218     {
219         MutexLocker locker(m_transactionInProgressMutex);
220         m_isTransactionQueueEnabled = false;
221         m_transactionInProgress = false;
222         m_transactionQueue.clear();
223     }
224
225     closeDatabase();
226
227     // Must ref() before calling databaseThread()->recordDatabaseClosed().
228     RefPtr<Database> protect = this;
229     databaseContext()->databaseThread()->recordDatabaseClosed(this);
230     databaseContext()->databaseThread()->unscheduleDatabaseTasks(this);
231     DatabaseTracker::tracker().removeOpenDatabase(this);
232 }
233
234 void Database::closeImmediately()
235 {
236     ASSERT(m_scriptExecutionContext->isContextThread());
237     DatabaseThread* databaseThread = databaseContext()->databaseThread();
238     if (databaseThread && !databaseThread->terminationRequested() && opened()) {
239         logErrorMessage("forcibly closing database");
240         databaseThread->scheduleImmediateTask(DatabaseCloseTask::create(this, 0));
241     }
242 }
243
244 unsigned long long Database::maximumSize() const
245 {
246     return DatabaseTracker::tracker().getMaxSizeForDatabase(this);
247 }
248
249 bool Database::performOpenAndVerify(bool setVersionInNewDatabase, ExceptionCode& e, String& errorMessage)
250 {
251     if (AbstractDatabase::performOpenAndVerify(setVersionInNewDatabase, e, errorMessage)) {
252         if (databaseContext()->databaseThread())
253             databaseContext()->databaseThread()->recordDatabaseOpen(this);
254
255         return true;
256     }
257
258     return false;
259 }
260
261 void Database::changeVersion(const String& oldVersion, const String& newVersion,
262                              PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback,
263                              PassRefPtr<VoidCallback> successCallback)
264 {
265     runTransaction(callback, errorCallback, successCallback, ChangeVersionWrapper::create(oldVersion, newVersion), false);
266 }
267
268 void Database::transaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback)
269 {
270     runTransaction(callback, errorCallback, successCallback, 0, false);
271 }
272
273 void Database::readTransaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback)
274 {
275     runTransaction(callback, errorCallback, successCallback, 0, true);
276 }
277
278 static void callTransactionErrorCallback(ScriptExecutionContext*, PassRefPtr<SQLTransactionErrorCallback> callback, PassRefPtr<SQLError> error)
279 {
280     callback->handleEvent(error.get());
281 }
282
283 void Database::runTransaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback,
284                               PassRefPtr<VoidCallback> successCallback, PassRefPtr<SQLTransactionWrapper> wrapper, bool readOnly)
285 {
286     MutexLocker locker(m_transactionInProgressMutex);
287     if (!m_isTransactionQueueEnabled) {
288         if (errorCallback) {
289             RefPtr<SQLError> error = SQLError::create(SQLError::UNKNOWN_ERR, "database has been closed");
290             scriptExecutionContext()->postTask(createCallbackTask(&callTransactionErrorCallback, errorCallback, error.release()));
291         }
292         return;
293     }
294     RefPtr<SQLTransaction> transaction = SQLTransaction::create(this, callback, errorCallback, successCallback, wrapper, readOnly);
295     m_transactionQueue.append(transaction.release());
296     if (!m_transactionInProgress)
297         scheduleTransaction();
298 }
299
300 void Database::inProgressTransactionCompleted()
301 {
302     MutexLocker locker(m_transactionInProgressMutex);
303     m_transactionInProgress = false;
304     scheduleTransaction();
305 }
306
307 void Database::scheduleTransaction()
308 {
309     ASSERT(!m_transactionInProgressMutex.tryLock()); // Locked by caller.
310     RefPtr<SQLTransaction> transaction;
311
312     if (m_isTransactionQueueEnabled && !m_transactionQueue.isEmpty())
313         transaction = m_transactionQueue.takeFirst();
314
315     if (transaction && databaseContext()->databaseThread()) {
316         OwnPtr<DatabaseTransactionTask> task = DatabaseTransactionTask::create(transaction);
317         LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for transaction %p\n", task.get(), task->transaction());
318         m_transactionInProgress = true;
319         databaseContext()->databaseThread()->scheduleTask(task.release());
320     } else
321         m_transactionInProgress = false;
322 }
323
324 void Database::scheduleTransactionStep(SQLTransaction* transaction, bool immediately)
325 {
326     if (!databaseContext()->databaseThread())
327         return;
328
329     OwnPtr<DatabaseTransactionTask> task = DatabaseTransactionTask::create(transaction);
330     LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for the transaction step\n", task.get());
331     if (immediately)
332         databaseContext()->databaseThread()->scheduleImmediateTask(task.release());
333     else
334         databaseContext()->databaseThread()->scheduleTask(task.release());
335 }
336
337 class DeliverPendingCallbackTask : public ScriptExecutionContext::Task {
338 public:
339     static PassOwnPtr<DeliverPendingCallbackTask> create(PassRefPtr<SQLTransaction> transaction)
340     {
341         return adoptPtr(new DeliverPendingCallbackTask(transaction));
342     }
343
344     virtual void performTask(ScriptExecutionContext*)
345     {
346         m_transaction->performPendingCallback();
347     }
348
349 private:
350     DeliverPendingCallbackTask(PassRefPtr<SQLTransaction> transaction)
351         : m_transaction(transaction)
352     {
353     }
354
355     RefPtr<SQLTransaction> m_transaction;
356 };
357
358 void Database::scheduleTransactionCallback(SQLTransaction* transaction)
359 {
360     m_scriptExecutionContext->postTask(DeliverPendingCallbackTask::create(transaction));
361 }
362
363 Vector<String> Database::performGetTableNames()
364 {
365     disableAuthorizer();
366
367     SQLiteStatement statement(sqliteDatabase(), "SELECT name FROM sqlite_master WHERE type='table';");
368     if (statement.prepare() != SQLResultOk) {
369         LOG_ERROR("Unable to retrieve list of tables for database %s", databaseDebugName().ascii().data());
370         enableAuthorizer();
371         return Vector<String>();
372     }
373
374     Vector<String> tableNames;
375     int result;
376     while ((result = statement.step()) == SQLResultRow) {
377         String name = statement.getColumnText(0);
378         if (name != databaseInfoTableName())
379             tableNames.append(name);
380     }
381
382     enableAuthorizer();
383
384     if (result != SQLResultDone) {
385         LOG_ERROR("Error getting tables for database %s", databaseDebugName().ascii().data());
386         return Vector<String>();
387     }
388
389     return tableNames;
390 }
391
392 SQLTransactionClient* Database::transactionClient() const
393 {
394     return databaseContext()->databaseThread()->transactionClient();
395 }
396
397 SQLTransactionCoordinator* Database::transactionCoordinator() const
398 {
399     return databaseContext()->databaseThread()->transactionCoordinator();
400 }
401
402 Vector<String> Database::tableNames()
403 {
404     // FIXME: Not using isolatedCopy on these strings looks ok since threads take strict turns
405     // in dealing with them. However, if the code changes, this may not be true anymore.
406     Vector<String> result;
407     DatabaseTaskSynchronizer synchronizer;
408     if (!databaseContext()->databaseThread() || databaseContext()->databaseThread()->terminationRequested(&synchronizer))
409         return result;
410
411     OwnPtr<DatabaseTableNamesTask> task = DatabaseTableNamesTask::create(this, &synchronizer, result);
412     databaseContext()->databaseThread()->scheduleImmediateTask(task.release());
413     synchronizer.waitForTaskCompletion();
414
415     return result;
416 }
417
418 SecurityOrigin* Database::securityOrigin() const
419 {
420     if (m_scriptExecutionContext->isContextThread())
421         return m_contextThreadSecurityOrigin.get();
422     if (currentThread() == databaseContext()->databaseThread()->getThreadID())
423         return m_databaseThreadSecurityOrigin.get();
424     return 0;
425 }
426
427 } // namespace WebCore
428
429 #endif // ENABLE(SQL_DATABASE)