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 "modules/webdatabase/Database.h"
31
32 #include "core/dom/CrossThreadTask.h"
33 #include "core/dom/Document.h"
34 #include "core/dom/ExecutionContext.h"
35 #include "core/html/VoidCallback.h"
36 #include "core/page/Page.h"
37 #include "platform/Logging.h"
38 #include "modules/webdatabase/sqlite/SQLiteStatement.h"
39 #include "modules/webdatabase/ChangeVersionData.h"
40 #include "modules/webdatabase/DatabaseCallback.h"
41 #include "modules/webdatabase/DatabaseContext.h"
42 #include "modules/webdatabase/DatabaseManager.h"
43 #include "modules/webdatabase/DatabaseTask.h"
44 #include "modules/webdatabase/DatabaseThread.h"
45 #include "modules/webdatabase/DatabaseTracker.h"
46 #include "modules/webdatabase/SQLError.h"
47 #include "modules/webdatabase/SQLTransaction.h"
48 #include "modules/webdatabase/SQLTransactionCallback.h"
49 #include "modules/webdatabase/SQLTransactionErrorCallback.h"
50 #include "platform/weborigin/SecurityOrigin.h"
51 #include "wtf/OwnPtr.h"
52 #include "wtf/PassOwnPtr.h"
53 #include "wtf/PassRefPtr.h"
54 #include "wtf/RefPtr.h"
55 #include "wtf/StdLibExtras.h"
56 #include "wtf/text/CString.h"
57
58 namespace WebCore {
59
create(ExecutionContext *,PassRefPtr<DatabaseBackendBase> backend)60 PassRefPtr<Database> Database::create(ExecutionContext*, PassRefPtr<DatabaseBackendBase> backend)
61 {
62 // FIXME: Currently, we're only simulating the backend by return the
63 // frontend database as its own the backend. When we split the 2 apart,
64 // this create() function should be changed to be a factory method for
65 // instantiating the backend.
66 return static_cast<Database*>(backend.get());
67 }
68
Database(PassRefPtr<DatabaseContext> databaseContext,const String & name,const String & expectedVersion,const String & displayName,unsigned long estimatedSize)69 Database::Database(PassRefPtr<DatabaseContext> databaseContext,
70 const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize)
71 : DatabaseBase(databaseContext->executionContext())
72 , DatabaseBackend(databaseContext, name, expectedVersion, displayName, estimatedSize)
73 , m_databaseContext(DatabaseBackend::databaseContext())
74 {
75 ScriptWrappable::init(this);
76 m_databaseThreadSecurityOrigin = m_contextThreadSecurityOrigin->isolatedCopy();
77 setFrontend(this);
78
79 ASSERT(m_databaseContext->databaseThread());
80 }
81
82 class DerefContextTask : public ExecutionContextTask {
83 public:
create(PassRefPtr<ExecutionContext> context)84 static PassOwnPtr<DerefContextTask> create(PassRefPtr<ExecutionContext> context)
85 {
86 return adoptPtr(new DerefContextTask(context));
87 }
88
performTask(ExecutionContext * context)89 virtual void performTask(ExecutionContext* context)
90 {
91 ASSERT_UNUSED(context, context == m_context);
92 m_context.clear();
93 }
94
isCleanupTask() const95 virtual bool isCleanupTask() const { return true; }
96
97 private:
DerefContextTask(PassRefPtr<ExecutionContext> context)98 DerefContextTask(PassRefPtr<ExecutionContext> context)
99 : m_context(context)
100 {
101 }
102
103 RefPtr<ExecutionContext> m_context;
104 };
105
~Database()106 Database::~Database()
107 {
108 // The reference to the ExecutionContext 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.
109 if (!m_executionContext->isContextThread()) {
110 // Grab a pointer to the script execution here because we're releasing it when we pass it to
111 // DerefContextTask::create.
112 ExecutionContext* executionContext = m_executionContext.get();
113
114 executionContext->postTask(DerefContextTask::create(m_executionContext.release()));
115 }
116 }
117
from(DatabaseBackend * backend)118 Database* Database::from(DatabaseBackend* backend)
119 {
120 return static_cast<Database*>(backend->m_frontend);
121 }
122
backend()123 PassRefPtr<DatabaseBackend> Database::backend()
124 {
125 return this;
126 }
127
version() const128 String Database::version() const
129 {
130 return DatabaseBackendBase::version();
131 }
132
closeImmediately()133 void Database::closeImmediately()
134 {
135 ASSERT(m_executionContext->isContextThread());
136 DatabaseThread* databaseThread = databaseContext()->databaseThread();
137 if (databaseThread && !databaseThread->terminationRequested() && opened()) {
138 logErrorMessage("forcibly closing database");
139 databaseThread->scheduleTask(DatabaseCloseTask::create(this, 0));
140 }
141 }
142
changeVersion(const String & oldVersion,const String & newVersion,PassOwnPtr<SQLTransactionCallback> callback,PassOwnPtr<SQLTransactionErrorCallback> errorCallback,PassOwnPtr<VoidCallback> successCallback)143 void Database::changeVersion(const String& oldVersion, const String& newVersion, PassOwnPtr<SQLTransactionCallback> callback, PassOwnPtr<SQLTransactionErrorCallback> errorCallback, PassOwnPtr<VoidCallback> successCallback)
144 {
145 ChangeVersionData data(oldVersion, newVersion);
146 runTransaction(callback, errorCallback, successCallback, false, &data);
147 }
148
transaction(PassOwnPtr<SQLTransactionCallback> callback,PassOwnPtr<SQLTransactionErrorCallback> errorCallback,PassOwnPtr<VoidCallback> successCallback)149 void Database::transaction(PassOwnPtr<SQLTransactionCallback> callback, PassOwnPtr<SQLTransactionErrorCallback> errorCallback, PassOwnPtr<VoidCallback> successCallback)
150 {
151 runTransaction(callback, errorCallback, successCallback, false);
152 }
153
readTransaction(PassOwnPtr<SQLTransactionCallback> callback,PassOwnPtr<SQLTransactionErrorCallback> errorCallback,PassOwnPtr<VoidCallback> successCallback)154 void Database::readTransaction(PassOwnPtr<SQLTransactionCallback> callback, PassOwnPtr<SQLTransactionErrorCallback> errorCallback, PassOwnPtr<VoidCallback> successCallback)
155 {
156 runTransaction(callback, errorCallback, successCallback, true);
157 }
158
callTransactionErrorCallback(ExecutionContext *,PassOwnPtr<SQLTransactionErrorCallback> callback,PassRefPtr<SQLError> error)159 static void callTransactionErrorCallback(ExecutionContext*, PassOwnPtr<SQLTransactionErrorCallback> callback, PassRefPtr<SQLError> error)
160 {
161 callback->handleEvent(error.get());
162 }
163
runTransaction(PassOwnPtr<SQLTransactionCallback> callback,PassOwnPtr<SQLTransactionErrorCallback> errorCallback,PassOwnPtr<VoidCallback> successCallback,bool readOnly,const ChangeVersionData * changeVersionData)164 void Database::runTransaction(PassOwnPtr<SQLTransactionCallback> callback, PassOwnPtr<SQLTransactionErrorCallback> errorCallback,
165 PassOwnPtr<VoidCallback> successCallback, bool readOnly, const ChangeVersionData* changeVersionData)
166 {
167 // FIXME: Rather than passing errorCallback to SQLTransaction and then sometimes firing it ourselves,
168 // this code should probably be pushed down into DatabaseBackend so that we only create the SQLTransaction
169 // if we're actually going to run it.
170 #if !ASSERT_DISABLED
171 SQLTransactionErrorCallback* originalErrorCallback = errorCallback.get();
172 #endif
173 RefPtr<SQLTransaction> transaction = SQLTransaction::create(this, callback, successCallback, errorCallback, readOnly);
174 RefPtr<SQLTransactionBackend> transactionBackend = backend()->runTransaction(transaction, readOnly, changeVersionData);
175 if (!transactionBackend) {
176 OwnPtr<SQLTransactionErrorCallback> callback = transaction->releaseErrorCallback();
177 ASSERT(callback == originalErrorCallback);
178 if (callback) {
179 RefPtr<SQLError> error = SQLError::create(SQLError::UNKNOWN_ERR, "database has been closed");
180 executionContext()->postTask(createCallbackTask(&callTransactionErrorCallback, callback.release(), error.release()));
181 }
182 }
183 }
184
185 class DeliverPendingCallbackTask : public ExecutionContextTask {
186 public:
create(PassRefPtr<SQLTransaction> transaction)187 static PassOwnPtr<DeliverPendingCallbackTask> create(PassRefPtr<SQLTransaction> transaction)
188 {
189 return adoptPtr(new DeliverPendingCallbackTask(transaction));
190 }
191
performTask(ExecutionContext *)192 virtual void performTask(ExecutionContext*)
193 {
194 m_transaction->performPendingCallback();
195 }
196
197 private:
DeliverPendingCallbackTask(PassRefPtr<SQLTransaction> transaction)198 DeliverPendingCallbackTask(PassRefPtr<SQLTransaction> transaction)
199 : m_transaction(transaction)
200 {
201 }
202
203 RefPtr<SQLTransaction> m_transaction;
204 };
205
scheduleTransactionCallback(SQLTransaction * transaction)206 void Database::scheduleTransactionCallback(SQLTransaction* transaction)
207 {
208 m_executionContext->postTask(DeliverPendingCallbackTask::create(transaction));
209 }
210
performGetTableNames()211 Vector<String> Database::performGetTableNames()
212 {
213 disableAuthorizer();
214
215 SQLiteStatement statement(sqliteDatabase(), "SELECT name FROM sqlite_master WHERE type='table';");
216 if (statement.prepare() != SQLResultOk) {
217 WTF_LOG_ERROR("Unable to retrieve list of tables for database %s", databaseDebugName().ascii().data());
218 enableAuthorizer();
219 return Vector<String>();
220 }
221
222 Vector<String> tableNames;
223 int result;
224 while ((result = statement.step()) == SQLResultRow) {
225 String name = statement.getColumnText(0);
226 if (name != databaseInfoTableName())
227 tableNames.append(name);
228 }
229
230 enableAuthorizer();
231
232 if (result != SQLResultDone) {
233 WTF_LOG_ERROR("Error getting tables for database %s", databaseDebugName().ascii().data());
234 return Vector<String>();
235 }
236
237 return tableNames;
238 }
239
tableNames()240 Vector<String> Database::tableNames()
241 {
242 // FIXME: Not using isolatedCopy on these strings looks ok since threads take strict turns
243 // in dealing with them. However, if the code changes, this may not be true anymore.
244 Vector<String> result;
245 DatabaseTaskSynchronizer synchronizer;
246 if (!databaseContext()->databaseThread() || databaseContext()->databaseThread()->terminationRequested(&synchronizer))
247 return result;
248
249 OwnPtr<DatabaseTableNamesTask> task = DatabaseTableNamesTask::create(this, &synchronizer, result);
250 databaseContext()->databaseThread()->scheduleTask(task.release());
251 synchronizer.waitForTaskCompletion();
252
253 return result;
254 }
255
securityOrigin() const256 SecurityOrigin* Database::securityOrigin() const
257 {
258 if (m_executionContext->isContextThread())
259 return m_contextThreadSecurityOrigin.get();
260 if (databaseContext()->databaseThread()->isDatabaseThread())
261 return m_databaseThreadSecurityOrigin.get();
262 return 0;
263 }
264
reportStartTransactionResult(int errorSite,int webSqlErrorCode,int sqliteErrorCode)265 void Database::reportStartTransactionResult(int errorSite, int webSqlErrorCode, int sqliteErrorCode)
266 {
267 backend()->reportStartTransactionResult(errorSite, webSqlErrorCode, sqliteErrorCode);
268 }
269
reportCommitTransactionResult(int errorSite,int webSqlErrorCode,int sqliteErrorCode)270 void Database::reportCommitTransactionResult(int errorSite, int webSqlErrorCode, int sqliteErrorCode)
271 {
272 backend()->reportCommitTransactionResult(errorSite, webSqlErrorCode, sqliteErrorCode);
273 }
274
275
276 } // namespace WebCore
277