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