1/*
2 * Copyright (C) 2007, 2008, 2013 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 "ChangeVersionData.h"
35#include "CrossThreadTask.h"
36#include "DatabaseBackendContext.h"
37#include "DatabaseCallback.h"
38#include "DatabaseContext.h"
39#include "DatabaseManager.h"
40#include "DatabaseTask.h"
41#include "DatabaseThread.h"
42#include "DatabaseTracker.h"
43#include "Document.h"
44#include "JSDOMWindow.h"
45#include "Logging.h"
46#include "NotImplemented.h"
47#include "Page.h"
48#include "SQLError.h"
49#include "SQLTransaction.h"
50#include "SQLTransactionCallback.h"
51#include "SQLTransactionErrorCallback.h"
52#include "SQLiteStatement.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
63namespace WebCore {
64
65PassRefPtr<Database> Database::create(ScriptExecutionContext*, PassRefPtr<DatabaseBackendBase> backend)
66{
67    // FIXME: Currently, we're only simulating the backend by return the
68    // frontend database as its own the backend. When we split the 2 apart,
69    // this create() function should be changed to be a factory method for
70    // instantiating the backend.
71    return static_cast<Database*>(backend.get());
72}
73
74Database::Database(PassRefPtr<DatabaseBackendContext> databaseContext,
75    const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize)
76    : DatabaseBase(databaseContext->scriptExecutionContext())
77    , DatabaseBackend(databaseContext, name, expectedVersion, displayName, estimatedSize)
78    , m_databaseContext(DatabaseBackend::databaseContext()->frontend())
79    , m_deleted(false)
80{
81    m_databaseThreadSecurityOrigin = m_contextThreadSecurityOrigin->isolatedCopy();
82    setFrontend(this);
83
84    ASSERT(m_databaseContext->databaseThread());
85}
86
87class DerefContextTask : public ScriptExecutionContext::Task {
88public:
89    static PassOwnPtr<DerefContextTask> create(PassRefPtr<ScriptExecutionContext> context)
90    {
91        return adoptPtr(new DerefContextTask(context));
92    }
93
94    virtual void performTask(ScriptExecutionContext* context)
95    {
96        ASSERT_UNUSED(context, context == m_context);
97        m_context.clear();
98    }
99
100    virtual bool isCleanupTask() const { return true; }
101
102private:
103    DerefContextTask(PassRefPtr<ScriptExecutionContext> context)
104        : m_context(context)
105    {
106    }
107
108    RefPtr<ScriptExecutionContext> m_context;
109};
110
111Database::~Database()
112{
113    // 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.
114    if (!m_scriptExecutionContext->isContextThread()) {
115        // Grab a pointer to the script execution here because we're releasing it when we pass it to
116        // DerefContextTask::create.
117        ScriptExecutionContext* scriptExecutionContext = m_scriptExecutionContext.get();
118
119        scriptExecutionContext->postTask(DerefContextTask::create(m_scriptExecutionContext.release()));
120    }
121}
122
123Database* Database::from(DatabaseBackend* backend)
124{
125    return static_cast<Database*>(backend->m_frontend);
126}
127
128PassRefPtr<DatabaseBackend> Database::backend()
129{
130    return this;
131}
132
133String Database::version() const
134{
135    if (m_deleted)
136        return String();
137    return DatabaseBackendBase::version();
138}
139
140void Database::markAsDeletedAndClose()
141{
142    if (m_deleted || !databaseContext()->databaseThread())
143        return;
144
145    LOG(StorageAPI, "Marking %s (%p) as deleted", stringIdentifier().ascii().data(), this);
146    m_deleted = true;
147
148    DatabaseTaskSynchronizer synchronizer;
149    if (databaseContext()->databaseThread()->terminationRequested(&synchronizer)) {
150        LOG(StorageAPI, "Database handle %p is on a terminated DatabaseThread, cannot be marked for normal closure\n", this);
151        return;
152    }
153
154    OwnPtr<DatabaseCloseTask> task = DatabaseCloseTask::create(this, &synchronizer);
155    databaseContext()->databaseThread()->scheduleImmediateTask(task.release());
156    synchronizer.waitForTaskCompletion();
157}
158
159void Database::closeImmediately()
160{
161    ASSERT(m_scriptExecutionContext->isContextThread());
162    DatabaseThread* databaseThread = databaseContext()->databaseThread();
163    if (databaseThread && !databaseThread->terminationRequested() && opened()) {
164        logErrorMessage("forcibly closing database");
165        databaseThread->scheduleImmediateTask(DatabaseCloseTask::create(this, 0));
166    }
167}
168
169void Database::changeVersion(const String& oldVersion, const String& newVersion,
170                             PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback,
171                             PassRefPtr<VoidCallback> successCallback)
172{
173    ChangeVersionData data(oldVersion, newVersion);
174    runTransaction(callback, errorCallback, successCallback, false, &data);
175}
176
177void Database::transaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback)
178{
179    runTransaction(callback, errorCallback, successCallback, false);
180}
181
182void Database::readTransaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback)
183{
184    runTransaction(callback, errorCallback, successCallback, true);
185}
186
187static void callTransactionErrorCallback(ScriptExecutionContext*, PassRefPtr<SQLTransactionErrorCallback> callback, PassRefPtr<SQLError> error)
188{
189    callback->handleEvent(error.get());
190}
191
192void Database::runTransaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback,
193    PassRefPtr<VoidCallback> successCallback, bool readOnly, const ChangeVersionData* changeVersionData)
194{
195    RefPtr<SQLTransactionErrorCallback> anotherRefToErrorCallback = errorCallback;
196    RefPtr<SQLTransaction> transaction = SQLTransaction::create(this, callback, successCallback, anotherRefToErrorCallback, readOnly);
197
198    RefPtr<SQLTransactionBackend> transactionBackend;
199    transactionBackend = backend()->runTransaction(transaction.release(), readOnly, changeVersionData);
200    if (!transactionBackend && anotherRefToErrorCallback) {
201        RefPtr<SQLError> error = SQLError::create(SQLError::UNKNOWN_ERR, "database has been closed");
202        scriptExecutionContext()->postTask(createCallbackTask(&callTransactionErrorCallback, anotherRefToErrorCallback, error.release()));
203    }
204}
205
206class DeliverPendingCallbackTask : public ScriptExecutionContext::Task {
207public:
208    static PassOwnPtr<DeliverPendingCallbackTask> create(PassRefPtr<SQLTransaction> transaction)
209    {
210        return adoptPtr(new DeliverPendingCallbackTask(transaction));
211    }
212
213    virtual void performTask(ScriptExecutionContext*)
214    {
215        m_transaction->performPendingCallback();
216    }
217
218private:
219    DeliverPendingCallbackTask(PassRefPtr<SQLTransaction> transaction)
220        : m_transaction(transaction)
221    {
222    }
223
224    RefPtr<SQLTransaction> m_transaction;
225};
226
227void Database::scheduleTransactionCallback(SQLTransaction* transaction)
228{
229    m_scriptExecutionContext->postTask(DeliverPendingCallbackTask::create(transaction));
230}
231
232Vector<String> Database::performGetTableNames()
233{
234    disableAuthorizer();
235
236    SQLiteStatement statement(sqliteDatabase(), "SELECT name FROM sqlite_master WHERE type='table';");
237    if (statement.prepare() != SQLResultOk) {
238        LOG_ERROR("Unable to retrieve list of tables for database %s", databaseDebugName().ascii().data());
239        enableAuthorizer();
240        return Vector<String>();
241    }
242
243    Vector<String> tableNames;
244    int result;
245    while ((result = statement.step()) == SQLResultRow) {
246        String name = statement.getColumnText(0);
247        if (name != databaseInfoTableName())
248            tableNames.append(name);
249    }
250
251    enableAuthorizer();
252
253    if (result != SQLResultDone) {
254        LOG_ERROR("Error getting tables for database %s", databaseDebugName().ascii().data());
255        return Vector<String>();
256    }
257
258    return tableNames;
259}
260
261Vector<String> Database::tableNames()
262{
263    // FIXME: Not using isolatedCopy on these strings looks ok since threads take strict turns
264    // in dealing with them. However, if the code changes, this may not be true anymore.
265    Vector<String> result;
266    DatabaseTaskSynchronizer synchronizer;
267    if (!databaseContext()->databaseThread() || databaseContext()->databaseThread()->terminationRequested(&synchronizer))
268        return result;
269
270    OwnPtr<DatabaseTableNamesTask> task = DatabaseTableNamesTask::create(this, &synchronizer, result);
271    databaseContext()->databaseThread()->scheduleImmediateTask(task.release());
272    synchronizer.waitForTaskCompletion();
273
274    return result;
275}
276
277SecurityOrigin* Database::securityOrigin() const
278{
279    if (m_scriptExecutionContext->isContextThread())
280        return m_contextThreadSecurityOrigin.get();
281    if (currentThread() == databaseContext()->databaseThread()->getThreadID())
282        return m_databaseThreadSecurityOrigin.get();
283    return 0;
284}
285
286} // namespace WebCore
287
288#endif // ENABLE(SQL_DATABASE)
289