Upstream version 10.38.222.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / modules / webdatabase / SQLTransactionBackendSync.cpp
1 /*
2  * Copyright (C) 2010 Google Inc. All rights reserved.
3  * Copyright (C) 2013 Apple Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *     * Neither the name of Google Inc. nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include "config.h"
33 #include "modules/webdatabase/SQLTransactionBackendSync.h"
34
35 #include "bindings/core/v8/ExceptionState.h"
36 #include "core/dom/ExceptionCode.h"
37 #include "core/dom/ExecutionContext.h"
38 #include "modules/webdatabase/sqlite/SQLValue.h"
39 #include "modules/webdatabase/sqlite/SQLiteTransaction.h"
40 #include "modules/webdatabase/DatabaseAuthorizer.h"
41 #include "modules/webdatabase/DatabaseContext.h"
42 #include "modules/webdatabase/DatabaseSync.h"
43 #include "modules/webdatabase/SQLError.h"
44 #include "modules/webdatabase/SQLResultSet.h"
45 #include "modules/webdatabase/SQLStatementSync.h"
46 #include "modules/webdatabase/SQLTransactionClient.h"
47 #include "modules/webdatabase/SQLTransactionSync.h"
48 #include "modules/webdatabase/SQLTransactionSyncCallback.h"
49 #include "wtf/PassRefPtr.h"
50 #include "wtf/RefPtr.h"
51
52 namespace blink {
53
54 SQLTransactionBackendSync::SQLTransactionBackendSync(DatabaseSync* db, PassOwnPtr<SQLTransactionSyncCallback> callback, bool readOnly)
55     : m_database(db)
56     , m_callback(callback)
57     , m_readOnly(readOnly)
58     , m_hasVersionMismatch(false)
59     , m_modifiedDatabase(false)
60     , m_transactionClient(adoptPtr(new SQLTransactionClient()))
61 {
62     ASSERT(m_database->executionContext()->isContextThread());
63 }
64
65 void SQLTransactionBackendSync::rollbackIfInProgress()
66 {
67     ASSERT(!m_database->executionContext() || m_database->executionContext()->isContextThread());
68     if (m_sqliteTransaction && m_sqliteTransaction->inProgress())
69         rollback();
70 }
71
72 SQLTransactionBackendSync::~SQLTransactionBackendSync()
73 {
74 #if ENABLE(OILPAN)
75     ASSERT(!m_sqliteTransaction || !m_sqliteTransaction->inProgress());
76 #else
77     rollbackIfInProgress();
78 #endif
79 }
80
81 void SQLTransactionBackendSync::trace(Visitor* visitor)
82 {
83     visitor->trace(m_database);
84 }
85
86 PassRefPtrWillBeRawPtr<SQLResultSet> SQLTransactionBackendSync::executeSQL(const String& sqlStatement, const Vector<SQLValue>& arguments, ExceptionState& exceptionState)
87 {
88     ASSERT(m_database->executionContext()->isContextThread());
89
90     m_database->setLastErrorMessage("");
91
92     if (!m_database->opened()) {
93         m_database->setLastErrorMessage("cannot executeSQL because the database is not open");
94         exceptionState.throwDOMException(UnknownError, SQLError::unknownErrorMessage);
95         return nullptr;
96     }
97
98     if (m_hasVersionMismatch) {
99         m_database->setLastErrorMessage("cannot executeSQL because there is a version mismatch");
100         exceptionState.throwDOMException(VersionError, SQLError::versionErrorMessage);
101         return nullptr;
102     }
103
104     if (sqlStatement.isEmpty())
105         return nullptr;
106
107     int permissions = DatabaseAuthorizer::ReadWriteMask;
108     if (!m_database->databaseContext()->allowDatabaseAccess())
109         permissions |= DatabaseAuthorizer::NoAccessMask;
110     else if (m_readOnly)
111         permissions |= DatabaseAuthorizer::ReadOnlyMask;
112
113     SQLStatementSync statement(sqlStatement, arguments, permissions);
114
115     m_database->resetAuthorizer();
116     bool retryStatement = true;
117     RefPtrWillBeRawPtr<SQLResultSet> resultSet;
118     while (retryStatement) {
119         retryStatement = false;
120         resultSet = statement.execute(m_database.get(), exceptionState);
121         if (!resultSet) {
122             if (m_sqliteTransaction->wasRolledBackBySqlite())
123                 return nullptr;
124
125             if (exceptionState.code() == QuotaExceededError) {
126                 if (m_transactionClient->didExceedQuota(database())) {
127                     exceptionState.clearException();
128                     retryStatement = true;
129                 } else {
130                     m_database->setLastErrorMessage("there was not enough remaining storage space");
131                     return nullptr;
132                 }
133             }
134         }
135     }
136
137     if (m_database->lastActionChangedDatabase())
138         m_modifiedDatabase = true;
139
140     return resultSet.release();
141 }
142
143 void SQLTransactionBackendSync::begin(ExceptionState& exceptionState)
144 {
145     ASSERT(m_database->executionContext()->isContextThread());
146     if (!m_database->opened()) {
147         m_database->reportStartTransactionResult(1, SQLError::UNKNOWN_ERR, 0);
148         m_database->setLastErrorMessage("cannot begin transaction because the database is not open");
149         exceptionState.throwDOMException(UnknownError, SQLError::unknownErrorMessage);
150         return;
151     }
152
153     ASSERT(!m_database->sqliteDatabase().transactionInProgress());
154
155     // Set the maximum usage for this transaction if this transactions is not read-only.
156     if (!m_readOnly)
157         m_database->sqliteDatabase().setMaximumSize(m_database->maximumSize());
158
159     ASSERT(!m_sqliteTransaction);
160     m_sqliteTransaction = adoptPtr(new SQLiteTransaction(m_database->sqliteDatabase(), m_readOnly));
161
162     m_database->resetDeletes();
163     m_database->disableAuthorizer();
164     m_sqliteTransaction->begin();
165     m_database->enableAuthorizer();
166
167     // Check if begin() succeeded.
168     if (!m_sqliteTransaction->inProgress()) {
169         ASSERT(!m_database->sqliteDatabase().transactionInProgress());
170         m_database->reportStartTransactionResult(2, SQLError::DATABASE_ERR, m_database->sqliteDatabase().lastError());
171         m_database->setLastErrorMessage("unable to begin transaction",
172             m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg());
173         m_sqliteTransaction.clear();
174         exceptionState.throwDOMException(SQLDatabaseError, "Unable to begin transaction.");
175         return;
176     }
177
178     // Note: We intentionally retrieve the actual version even with an empty expected version.
179     // In multi-process browsers, we take this opportinutiy to update the cached value for
180     // the actual version. In single-process browsers, this is just a map lookup.
181     String actualVersion;
182     if (!m_database->getActualVersionForTransaction(actualVersion)) {
183         m_database->reportStartTransactionResult(3, SQLError::DATABASE_ERR, m_database->sqliteDatabase().lastError());
184         m_database->setLastErrorMessage("unable to read version",
185             m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg());
186         rollback();
187         exceptionState.throwDOMException(SQLDatabaseError, "Unable to read version.");
188         return;
189     }
190     m_hasVersionMismatch = !m_database->expectedVersion().isEmpty() && (m_database->expectedVersion() != actualVersion);
191     m_database->reportStartTransactionResult(0, -1, 0); // OK
192 }
193
194 void SQLTransactionBackendSync::execute(ExceptionState& exceptionState)
195 {
196     ASSERT(m_database->executionContext()->isContextThread());
197     if (!m_database->opened() || (m_callback && !m_callback->handleEvent(SQLTransactionSync::from(this)))) {
198         if (m_database->lastErrorMessage().isEmpty())
199             m_database->setLastErrorMessage("failed to execute transaction callback");
200         m_callback.clear();
201         exceptionState.throwDOMException(UnknownError, SQLError::unknownErrorMessage);
202         return;
203     }
204
205     m_callback.clear();
206 }
207
208 void SQLTransactionBackendSync::commit(ExceptionState& exceptionState)
209 {
210     ASSERT(m_database->executionContext()->isContextThread());
211     if (!m_database->opened()) {
212         m_database->reportCommitTransactionResult(1, SQLError::UNKNOWN_ERR, 0);
213         m_database->setLastErrorMessage("unable to commit transaction because the database is not open.");
214         exceptionState.throwDOMException(UnknownError, SQLError::unknownErrorMessage);
215         return;
216     }
217
218     ASSERT(m_sqliteTransaction);
219
220     m_database->disableAuthorizer();
221     m_sqliteTransaction->commit();
222     m_database->enableAuthorizer();
223
224     // If the commit failed, the transaction will still be marked as "in progress"
225     if (m_sqliteTransaction->inProgress()) {
226         m_database->reportCommitTransactionResult(2, SQLError::DATABASE_ERR, m_database->sqliteDatabase().lastError());
227         m_database->setLastErrorMessage("unable to commit transaction",
228             m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg());
229         exceptionState.throwDOMException(SQLDatabaseError, "Unable to commit transaction.");
230         return;
231     }
232
233     m_sqliteTransaction.clear();
234
235     // Vacuum the database if anything was deleted.
236     if (m_database->hadDeletes())
237         m_database->incrementalVacuumIfNeeded();
238
239     // The commit was successful. If the transaction modified this database, notify the delegates.
240     if (m_modifiedDatabase)
241         m_transactionClient->didCommitWriteTransaction(database());
242
243     m_database->reportCommitTransactionResult(0, -1, 0); // OK
244 }
245
246 // This can be called during GC. Do not allocate new on-heap objects.
247 void SQLTransactionBackendSync::rollback()
248 {
249     ASSERT(!m_database->executionContext() || m_database->executionContext()->isContextThread());
250     m_database->disableAuthorizer();
251     if (m_sqliteTransaction) {
252         m_sqliteTransaction->rollback();
253         m_sqliteTransaction.clear();
254     }
255     m_database->enableAuthorizer();
256
257     ASSERT(!m_database->sqliteDatabase().transactionInProgress());
258 }
259
260 } // namespace blink