Database.cpp revision 2fc2651226baac27029e38c9d6ef883fa32084db
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
63namespace WebCore {
64
65class DatabaseCreationCallbackTask : public ScriptExecutionContext::Task {
66public:
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
77private:
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
88PassRefPtr<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
125Database::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
137class DerefContextTask : public ScriptExecutionContext::Task {
138public:
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
152private:
153    DerefContextTask(PassRefPtr<ScriptExecutionContext> context)
154        : m_context(context)
155    {
156    }
157
158    RefPtr<ScriptExecutionContext> m_context;
159};
160
161Database::~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
173String Database::version() const
174{
175    if (m_deleted)
176        return String();
177    return AbstractDatabase::version();
178}
179
180bool 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
194void 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
213void 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
233void Database::closeImmediately()
234{
235    DatabaseThread* databaseThread = scriptExecutionContext()->databaseThread();
236    if (databaseThread && !databaseThread->terminationRequested() && opened())
237        databaseThread->scheduleImmediateTask(DatabaseCloseTask::create(this, 0));
238}
239
240unsigned long long Database::maximumSize() const
241{
242    return DatabaseTracker::tracker().getMaxSizeForDatabase(this);
243}
244
245bool 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
257void Database::changeVersion(const String& oldVersion, const String& newVersion,
258                             PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback,
259                             PassRefPtr<VoidCallback> successCallback)
260{
261    m_transactionQueue.append(SQLTransaction::create(this, callback, errorCallback, successCallback, ChangeVersionWrapper::create(oldVersion, newVersion)));
262    MutexLocker locker(m_transactionInProgressMutex);
263    if (!m_transactionInProgress)
264        scheduleTransaction();
265}
266
267void Database::transaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback)
268{
269    runTransaction(callback, errorCallback, successCallback, false);
270}
271
272void Database::readTransaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback)
273{
274    runTransaction(callback, errorCallback, successCallback, true);
275}
276
277void Database::runTransaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback,
278                              PassRefPtr<VoidCallback> successCallback, bool readOnly)
279{
280    m_transactionQueue.append(SQLTransaction::create(this, callback, errorCallback, successCallback, 0, readOnly));
281    MutexLocker locker(m_transactionInProgressMutex);
282    if (!m_transactionInProgress)
283        scheduleTransaction();
284}
285
286void Database::inProgressTransactionCompleted()
287{
288    MutexLocker locker(m_transactionInProgressMutex);
289    m_transactionInProgress = false;
290    scheduleTransaction();
291}
292
293void Database::scheduleTransaction()
294{
295    ASSERT(!m_transactionInProgressMutex.tryLock()); // Locked by caller.
296    RefPtr<SQLTransaction> transaction;
297
298    if (m_isTransactionQueueEnabled && !m_transactionQueue.isEmpty()) {
299        transaction = m_transactionQueue.takeFirst();
300    }
301
302    if (transaction && m_scriptExecutionContext->databaseThread()) {
303        OwnPtr<DatabaseTransactionTask> task = DatabaseTransactionTask::create(transaction);
304        LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for transaction %p\n", task.get(), task->transaction());
305        m_transactionInProgress = true;
306        m_scriptExecutionContext->databaseThread()->scheduleTask(task.release());
307    } else
308        m_transactionInProgress = false;
309}
310
311void Database::scheduleTransactionStep(SQLTransaction* transaction, bool immediately)
312{
313    if (!m_scriptExecutionContext->databaseThread())
314        return;
315
316    OwnPtr<DatabaseTransactionTask> task = DatabaseTransactionTask::create(transaction);
317    LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for the transaction step\n", task.get());
318    if (immediately)
319        m_scriptExecutionContext->databaseThread()->scheduleImmediateTask(task.release());
320    else
321        m_scriptExecutionContext->databaseThread()->scheduleTask(task.release());
322}
323
324class DeliverPendingCallbackTask : public ScriptExecutionContext::Task {
325public:
326    static PassOwnPtr<DeliverPendingCallbackTask> create(PassRefPtr<SQLTransaction> transaction)
327    {
328        return adoptPtr(new DeliverPendingCallbackTask(transaction));
329    }
330
331    virtual void performTask(ScriptExecutionContext*)
332    {
333        m_transaction->performPendingCallback();
334    }
335
336private:
337    DeliverPendingCallbackTask(PassRefPtr<SQLTransaction> transaction)
338        : m_transaction(transaction)
339    {
340    }
341
342    RefPtr<SQLTransaction> m_transaction;
343};
344
345void Database::scheduleTransactionCallback(SQLTransaction* transaction)
346{
347    m_scriptExecutionContext->postTask(DeliverPendingCallbackTask::create(transaction));
348}
349
350Vector<String> Database::performGetTableNames()
351{
352    disableAuthorizer();
353
354    SQLiteStatement statement(sqliteDatabase(), "SELECT name FROM sqlite_master WHERE type='table';");
355    if (statement.prepare() != SQLResultOk) {
356        LOG_ERROR("Unable to retrieve list of tables for database %s", databaseDebugName().ascii().data());
357        enableAuthorizer();
358        return Vector<String>();
359    }
360
361    Vector<String> tableNames;
362    int result;
363    while ((result = statement.step()) == SQLResultRow) {
364        String name = statement.getColumnText(0);
365        if (name != databaseInfoTableName())
366            tableNames.append(name);
367    }
368
369    enableAuthorizer();
370
371    if (result != SQLResultDone) {
372        LOG_ERROR("Error getting tables for database %s", databaseDebugName().ascii().data());
373        return Vector<String>();
374    }
375
376    return tableNames;
377}
378
379SQLTransactionClient* Database::transactionClient() const
380{
381    return m_scriptExecutionContext->databaseThread()->transactionClient();
382}
383
384SQLTransactionCoordinator* Database::transactionCoordinator() const
385{
386    return m_scriptExecutionContext->databaseThread()->transactionCoordinator();
387}
388
389Vector<String> Database::tableNames()
390{
391    // FIXME: Not using threadsafeCopy on these strings looks ok since threads take strict turns
392    // in dealing with them. However, if the code changes, this may not be true anymore.
393    Vector<String> result;
394    DatabaseTaskSynchronizer synchronizer;
395    if (!m_scriptExecutionContext->databaseThread() || m_scriptExecutionContext->databaseThread()->terminationRequested(&synchronizer))
396        return result;
397
398    OwnPtr<DatabaseTableNamesTask> task = DatabaseTableNamesTask::create(this, &synchronizer, result);
399    m_scriptExecutionContext->databaseThread()->scheduleImmediateTask(task.release());
400    synchronizer.waitForTaskCompletion();
401
402    return result;
403}
404
405SecurityOrigin* Database::securityOrigin() const
406{
407    if (m_scriptExecutionContext->isContextThread())
408        return m_contextThreadSecurityOrigin.get();
409    if (currentThread() == m_scriptExecutionContext->databaseThread()->getThreadID())
410        return m_databaseThreadSecurityOrigin.get();
411    return 0;
412}
413
414} // namespace WebCore
415
416#endif // ENABLE(DATABASE)
417