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 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 "DatabaseBackendContext.h"
36#include "DatabaseCallback.h"
37#include "DatabaseContext.h"
38#include "DatabaseManager.h"
39#include "DatabaseTask.h"
40#include "DatabaseThread.h"
41#include "DatabaseTracker.h"
42#include "Document.h"
43#include "JSDOMWindow.h"
44#include "Logging.h"
45#include "Page.h"
46#include "SQLError.h"
47#include "SQLTransaction.h"
48#include "SQLTransactionCallback.h"
49#include "SQLTransactionErrorCallback.h"
50#include "SQLiteStatement.h"
51#include "ScriptExecutionContext.h"
52#include "SecurityOrigin.h"
53#include "VoidCallback.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 PLATFORM(IOS)
60#include "SQLiteDatabaseTracker.h"
61#endif
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
87Database::~Database()
88{
89    // 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.
90    if (!m_scriptExecutionContext->isContextThread()) {
91        // Grab a pointer to the script execution here because we're releasing it when we pass it to
92        // DerefContextTask::create.
93        PassRefPtr<ScriptExecutionContext> passedContext = m_scriptExecutionContext.release();
94        passedContext->postTask({ScriptExecutionContext::Task::CleanupTask, [=] (ScriptExecutionContext& context) {
95            ASSERT_UNUSED(context, &context == passedContext);
96            RefPtr<ScriptExecutionContext> scriptExecutionContext(passedContext);
97        }});
98    }
99}
100
101Database* Database::from(DatabaseBackend* backend)
102{
103    return static_cast<Database*>(backend->m_frontend);
104}
105
106PassRefPtr<DatabaseBackend> Database::backend()
107{
108    return this;
109}
110
111String Database::version() const
112{
113    if (m_deleted)
114        return String();
115    return DatabaseBackendBase::version();
116}
117
118void Database::markAsDeletedAndClose()
119{
120    if (m_deleted || !databaseContext()->databaseThread())
121        return;
122
123    LOG(StorageAPI, "Marking %s (%p) as deleted", stringIdentifier().ascii().data(), this);
124    m_deleted = true;
125
126    DatabaseTaskSynchronizer synchronizer;
127    if (databaseContext()->databaseThread()->terminationRequested(&synchronizer)) {
128        LOG(StorageAPI, "Database handle %p is on a terminated DatabaseThread, cannot be marked for normal closure\n", this);
129        return;
130    }
131
132    auto task = DatabaseCloseTask::create(this, &synchronizer);
133    databaseContext()->databaseThread()->scheduleImmediateTask(WTF::move(task));
134    synchronizer.waitForTaskCompletion();
135}
136
137void Database::closeImmediately()
138{
139    ASSERT(m_scriptExecutionContext->isContextThread());
140    DatabaseThread* databaseThread = databaseContext()->databaseThread();
141    if (databaseThread && !databaseThread->terminationRequested() && opened()) {
142        logErrorMessage("forcibly closing database");
143        databaseThread->scheduleImmediateTask(DatabaseCloseTask::create(this, 0));
144    }
145}
146
147void Database::changeVersion(const String& oldVersion, const String& newVersion,
148                             PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback,
149                             PassRefPtr<VoidCallback> successCallback)
150{
151    ChangeVersionData data(oldVersion, newVersion);
152    runTransaction(callback, errorCallback, successCallback, false, &data);
153}
154
155void Database::transaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback)
156{
157    runTransaction(callback, errorCallback, successCallback, false);
158}
159
160void Database::readTransaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback)
161{
162    runTransaction(callback, errorCallback, successCallback, true);
163}
164
165void Database::runTransaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback,
166    PassRefPtr<VoidCallback> successCallback, bool readOnly, const ChangeVersionData* changeVersionData)
167{
168    RefPtr<SQLTransactionErrorCallback> errorCallbackProtector(errorCallback);
169    RefPtr<SQLTransaction> transaction = SQLTransaction::create(this, callback, successCallback, errorCallbackProtector, readOnly);
170
171    RefPtr<SQLTransactionBackend> transactionBackend(backend()->runTransaction(transaction.release(), readOnly, changeVersionData));
172    if (!transactionBackend && errorCallbackProtector)
173        scriptExecutionContext()->postTask([=] (ScriptExecutionContext&) {
174            errorCallbackProtector->handleEvent(SQLError::create(SQLError::UNKNOWN_ERR, "database has been closed").get());
175        });
176}
177
178void Database::scheduleTransactionCallback(SQLTransaction* transaction)
179{
180    RefPtr<SQLTransaction> transactionProtector(transaction);
181    m_scriptExecutionContext->postTask([=] (ScriptExecutionContext&) {
182        transactionProtector->performPendingCallback();
183    });
184}
185
186Vector<String> Database::performGetTableNames()
187{
188    disableAuthorizer();
189
190    SQLiteStatement statement(sqliteDatabase(), "SELECT name FROM sqlite_master WHERE type='table';");
191    if (statement.prepare() != SQLResultOk) {
192        LOG_ERROR("Unable to retrieve list of tables for database %s", databaseDebugName().ascii().data());
193        enableAuthorizer();
194        return Vector<String>();
195    }
196
197    Vector<String> tableNames;
198    int result;
199    while ((result = statement.step()) == SQLResultRow) {
200        String name = statement.getColumnText(0);
201        if (name != databaseInfoTableName())
202            tableNames.append(name);
203    }
204
205    enableAuthorizer();
206
207    if (result != SQLResultDone) {
208        LOG_ERROR("Error getting tables for database %s", databaseDebugName().ascii().data());
209        return Vector<String>();
210    }
211
212    return tableNames;
213}
214
215Vector<String> Database::tableNames()
216{
217    // FIXME: Not using isolatedCopy on these strings looks ok since threads take strict turns
218    // in dealing with them. However, if the code changes, this may not be true anymore.
219    Vector<String> result;
220    DatabaseTaskSynchronizer synchronizer;
221    if (!databaseContext()->databaseThread() || databaseContext()->databaseThread()->terminationRequested(&synchronizer))
222        return result;
223
224    auto task = DatabaseTableNamesTask::create(this, &synchronizer, result);
225    databaseContext()->databaseThread()->scheduleImmediateTask(WTF::move(task));
226    synchronizer.waitForTaskCompletion();
227
228    return result;
229}
230
231SecurityOrigin* Database::securityOrigin() const
232{
233    if (m_scriptExecutionContext->isContextThread())
234        return m_contextThreadSecurityOrigin.get();
235    if (currentThread() == databaseContext()->databaseThread()->getThreadID())
236        return m_databaseThreadSecurityOrigin.get();
237    return 0;
238}
239
240} // namespace WebCore
241
242#endif // ENABLE(SQL_DATABASE)
243