a6c2f3d239b82880332daa8d27f8c902b835b0f7
[WebKit-https.git] / Source / WebCore / Modules / indexeddb / server / UniqueIDBDatabase.cpp
1 /*
2  * Copyright (C) 2015 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  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "UniqueIDBDatabase.h"
28
29 #if ENABLE(INDEXED_DATABASE)
30
31 #include "IDBResultData.h"
32 #include "IDBServer.h"
33 #include "Logging.h"
34 #include "UniqueIDBDatabaseConnection.h"
35 #include <wtf/MainThread.h>
36 #include <wtf/ThreadSafeRefCounted.h>
37
38 namespace WebCore {
39 namespace IDBServer {
40     
41 UniqueIDBDatabase::UniqueIDBDatabase(IDBServer& server, const IDBDatabaseIdentifier& identifier)
42     : m_server(server)
43     , m_identifier(identifier)
44 {
45 }
46
47 const IDBDatabaseInfo& UniqueIDBDatabase::info() const
48 {
49     RELEASE_ASSERT(m_databaseInfo);
50     return *m_databaseInfo;
51 }
52
53 void UniqueIDBDatabase::openDatabaseConnection(IDBConnectionToClient& connection, const IDBRequestData& requestData)
54 {
55     auto operation = IDBServerOperation::create(connection, requestData);
56     m_pendingOpenDatabaseOperations.append(WTF::move(operation));
57
58     if (m_databaseInfo) {
59         handleOpenDatabaseOperations();
60         return;
61     }
62     
63     m_server.postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::openBackingStore, m_identifier));
64 }
65
66 void UniqueIDBDatabase::handleOpenDatabaseOperations()
67 {
68     ASSERT(isMainThread());
69     LOG(IndexedDB, "(main) UniqueIDBDatabase::handleOpenDatabaseOperations");
70
71     // If a version change transaction is currently in progress, no new connections can be opened right now.
72     // We will try again later.
73     if (m_versionChangeDatabaseConnection)
74         return;
75
76     auto operation = m_pendingOpenDatabaseOperations.takeFirst();
77
78     // 3.3.1 Opening a database
79     // If requested version is undefined, then let requested version be 1 if db was created in the previous step,
80     // or the current version of db otherwise.
81     uint64_t requestedVersion = operation->requestData().requestedVersion();
82     if (!requestedVersion)
83         requestedVersion = m_databaseInfo->version() ? m_databaseInfo->version() : 1;
84
85     // 3.3.1 Opening a database
86     // If the database version higher than the requested version, abort these steps and return a VersionError.
87     if (requestedVersion < m_databaseInfo->version()) {
88         auto result = IDBResultData::error(operation->requestData().requestIdentifier(), IDBError(IDBExceptionCode::VersionError));
89         operation->connection().didOpenDatabase(result);
90         return;
91     }
92
93     Ref<UniqueIDBDatabaseConnection> connection = UniqueIDBDatabaseConnection::create(*this, operation->connection());
94     UniqueIDBDatabaseConnection* rawConnection = &connection.get();
95
96     if (requestedVersion == m_databaseInfo->version()) {
97         addOpenDatabaseConnection(WTF::move(connection));
98
99         auto result = IDBResultData::openDatabaseSuccess(operation->requestData().requestIdentifier(), *rawConnection);
100         operation->connection().didOpenDatabase(result);
101         return;
102     }
103
104     ASSERT(!m_versionChangeOperation);
105     ASSERT(!m_versionChangeDatabaseConnection);
106
107     m_versionChangeOperation = adoptRef(operation.leakRef());
108     m_versionChangeDatabaseConnection = rawConnection;
109
110     // 3.3.7 "versionchange" transaction steps
111     // If there's no other open connections to this database, the version change process can begin immediately.
112     if (!hasAnyOpenConnections()) {
113         startVersionChangeTransaction();
114         return;
115     }
116
117     // Otherwise we have to notify all those open connections and wait for them to close.
118     notifyConnectionsOfVersionChange();
119 }
120
121 bool UniqueIDBDatabase::hasAnyOpenConnections() const
122 {
123     return !m_openDatabaseConnections.isEmpty();
124 }
125
126 static uint64_t generateUniqueCallbackIdentifier()
127 {
128     ASSERT(isMainThread());
129     static uint64_t currentID = 0;
130     return ++currentID;
131 }
132
133 uint64_t UniqueIDBDatabase::storeCallback(ErrorCallback callback)
134 {
135     uint64_t identifier = generateUniqueCallbackIdentifier();
136     m_errorCallbacks.set(identifier, callback);
137     return identifier;
138 }
139
140 void UniqueIDBDatabase::startVersionChangeTransaction()
141 {
142     LOG(IndexedDB, "(main) UniqueIDBDatabase::startVersionChangeTransaction");
143
144     ASSERT(!m_versionChangeTransaction);
145     ASSERT(m_versionChangeOperation);
146     ASSERT(m_versionChangeDatabaseConnection);
147
148     auto operation = m_versionChangeOperation;
149     m_versionChangeOperation = nullptr;
150
151     uint64_t requestedVersion = operation->requestData().requestedVersion();
152     if (!requestedVersion)
153         requestedVersion = m_databaseInfo->version() ? m_databaseInfo->version() : 1;
154
155     addOpenDatabaseConnection(*m_versionChangeDatabaseConnection);
156
157     m_versionChangeTransaction = &m_versionChangeDatabaseConnection->createVersionChangeTransaction(requestedVersion);
158
159     auto result = IDBResultData::openDatabaseUpgradeNeeded(operation->requestData().requestIdentifier(), *m_versionChangeTransaction);
160     operation->connection().didOpenDatabase(result);
161 }
162
163 void UniqueIDBDatabase::notifyConnectionsOfVersionChange()
164 {
165     ASSERT(m_versionChangeOperation);
166     ASSERT(m_versionChangeDatabaseConnection);
167
168     uint64_t requestedVersion = m_versionChangeOperation->requestData().requestedVersion();
169
170     LOG(IndexedDB, "(main) UniqueIDBDatabase::notifyConnectionsOfVersionChange - %llu", static_cast<unsigned long long>(requestedVersion));
171
172     // 3.3.7 "versionchange" transaction steps
173     // Fire a versionchange event at each connection in m_openDatabaseConnections that is open.
174     // The event must not be fired on connections which has the closePending flag set.
175     for (auto connection : m_openDatabaseConnections) {
176         if (connection->closePending())
177             continue;
178
179         connection->fireVersionChangeEvent(requestedVersion);
180     }
181 }
182
183 void UniqueIDBDatabase::addOpenDatabaseConnection(Ref<UniqueIDBDatabaseConnection>&& connection)
184 {
185     ASSERT(!m_openDatabaseConnections.contains(&connection.get()));
186     m_openDatabaseConnections.add(adoptRef(connection.leakRef()));
187 }
188
189 void UniqueIDBDatabase::openBackingStore(const IDBDatabaseIdentifier& identifier)
190 {
191     ASSERT(!isMainThread());
192     LOG(IndexedDB, "(db) UniqueIDBDatabase::openBackingStore");
193
194     ASSERT(!m_backingStore);
195     m_backingStore = m_server.createBackingStore(identifier);
196     auto databaseInfo = m_backingStore->getOrEstablishDatabaseInfo();
197
198     m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didOpenBackingStore, databaseInfo));
199 }
200
201 void UniqueIDBDatabase::didOpenBackingStore(const IDBDatabaseInfo& info)
202 {
203     ASSERT(isMainThread());
204     LOG(IndexedDB, "(main) UniqueIDBDatabase::didOpenBackingStore");
205     
206     m_databaseInfo = std::make_unique<IDBDatabaseInfo>(info);
207
208     handleOpenDatabaseOperations();
209 }
210
211 void UniqueIDBDatabase::commitTransaction(UniqueIDBDatabaseTransaction& transaction, ErrorCallback callback)
212 {
213     ASSERT(isMainThread());
214     LOG(IndexedDB, "(main) UniqueIDBDatabase::commitTransaction");
215
216     if (m_versionChangeTransaction == &transaction) {
217         ASSERT(&m_versionChangeTransaction->databaseConnection() == m_versionChangeDatabaseConnection);
218         m_databaseInfo->setVersion(transaction.info().newVersion());
219         m_versionChangeTransaction = nullptr;
220         m_versionChangeDatabaseConnection = nullptr;
221     }
222
223     uint64_t callbackID = storeCallback(callback);
224     m_server.postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performCommitTransaction, callbackID, transaction.info().identifier()));
225 }
226
227 void UniqueIDBDatabase::performCommitTransaction(uint64_t callbackIdentifier, const IDBResourceIdentifier&)
228 {
229     ASSERT(!isMainThread());
230     LOG(IndexedDB, "(db) UniqueIDBDatabase::performCommitTransaction");
231
232     // FIXME: Commit transaction in backing store, once that exists.
233
234     IDBError error;
235     m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformCommitTransaction, callbackIdentifier, error));
236
237 }
238
239 void UniqueIDBDatabase::didPerformCommitTransaction(uint64_t callbackIdentifier, const IDBError& error)
240 {
241     ASSERT(isMainThread());
242     LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformCommitTransaction");
243
244     performErrorCallback(callbackIdentifier, error);
245 }
246
247 void UniqueIDBDatabase::transactionDestroyed(UniqueIDBDatabaseTransaction& transaction)
248 {
249     if (m_versionChangeTransaction == &transaction)
250         m_versionChangeTransaction = nullptr;
251 }
252
253 void UniqueIDBDatabase::connectionClosedFromClient(UniqueIDBDatabaseConnection& connection)
254 {
255     ASSERT(isMainThread());
256     LOG(IndexedDB, "(main) UniqueIDBDatabase::connectionClosedFromClient");
257
258     if (m_versionChangeDatabaseConnection == &connection)
259         m_versionChangeDatabaseConnection = nullptr;
260
261     ASSERT(m_openDatabaseConnections.contains(&connection));
262
263     auto removedConnection = m_openDatabaseConnections.take(&connection);
264     if (removedConnection->hasNonFinishedTransactions()) {
265         m_closePendingDatabaseConnections.add(WTF::move(removedConnection));
266         return;
267     }
268
269     // FIXME: Now that a database connection has closed, previously blocked transactions might be runnable.
270     // Try to run them now.
271 }
272
273 void UniqueIDBDatabase::performErrorCallback(uint64_t callbackIdentifier, const IDBError& error)
274 {
275     auto callback = m_errorCallbacks.take(callbackIdentifier);
276     ASSERT(callback);
277     callback(error);
278 }
279
280 } // namespace IDBServer
281 } // namespace WebCore
282
283 #endif // ENABLE(INDEXED_DATABASE)