Modern IDB: Basic createObjectStore implementation.
[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 "IDBTransactionInfo.h"
34 #include "Logging.h"
35 #include "UniqueIDBDatabaseConnection.h"
36 #include <wtf/MainThread.h>
37 #include <wtf/ThreadSafeRefCounted.h>
38
39 namespace WebCore {
40 namespace IDBServer {
41     
42 UniqueIDBDatabase::UniqueIDBDatabase(IDBServer& server, const IDBDatabaseIdentifier& identifier)
43     : m_server(server)
44     , m_identifier(identifier)
45     , m_transactionSchedulingTimer(*this, &UniqueIDBDatabase::transactionSchedulingTimerFired)
46 {
47 }
48
49 const IDBDatabaseInfo& UniqueIDBDatabase::info() const
50 {
51     RELEASE_ASSERT(m_databaseInfo);
52     return *m_databaseInfo;
53 }
54
55 void UniqueIDBDatabase::openDatabaseConnection(IDBConnectionToClient& connection, const IDBRequestData& requestData)
56 {
57     auto operation = IDBServerOperation::create(connection, requestData);
58     m_pendingOpenDatabaseOperations.append(WTF::move(operation));
59
60     if (m_databaseInfo) {
61         handleOpenDatabaseOperations();
62         return;
63     }
64     
65     m_server.postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::openBackingStore, m_identifier));
66 }
67
68 void UniqueIDBDatabase::handleOpenDatabaseOperations()
69 {
70     ASSERT(isMainThread());
71     LOG(IndexedDB, "(main) UniqueIDBDatabase::handleOpenDatabaseOperations");
72
73     // If a version change transaction is currently in progress, no new connections can be opened right now.
74     // We will try again later.
75     if (m_versionChangeDatabaseConnection)
76         return;
77
78     auto operation = m_pendingOpenDatabaseOperations.takeFirst();
79
80     // 3.3.1 Opening a database
81     // If requested version is undefined, then let requested version be 1 if db was created in the previous step,
82     // or the current version of db otherwise.
83     uint64_t requestedVersion = operation->requestData().requestedVersion();
84     if (!requestedVersion)
85         requestedVersion = m_databaseInfo->version() ? m_databaseInfo->version() : 1;
86
87     // 3.3.1 Opening a database
88     // If the database version higher than the requested version, abort these steps and return a VersionError.
89     if (requestedVersion < m_databaseInfo->version()) {
90         auto result = IDBResultData::error(operation->requestData().requestIdentifier(), IDBError(IDBExceptionCode::VersionError));
91         operation->connection().didOpenDatabase(result);
92         return;
93     }
94
95     Ref<UniqueIDBDatabaseConnection> connection = UniqueIDBDatabaseConnection::create(*this, operation->connection());
96     UniqueIDBDatabaseConnection* rawConnection = &connection.get();
97
98     if (requestedVersion == m_databaseInfo->version()) {
99         addOpenDatabaseConnection(WTF::move(connection));
100
101         auto result = IDBResultData::openDatabaseSuccess(operation->requestData().requestIdentifier(), *rawConnection);
102         operation->connection().didOpenDatabase(result);
103         return;
104     }
105
106     ASSERT(!m_versionChangeOperation);
107     ASSERT(!m_versionChangeDatabaseConnection);
108
109     m_versionChangeOperation = adoptRef(operation.leakRef());
110     m_versionChangeDatabaseConnection = rawConnection;
111
112     // 3.3.7 "versionchange" transaction steps
113     // If there's no other open connections to this database, the version change process can begin immediately.
114     if (!hasAnyOpenConnections()) {
115         startVersionChangeTransaction();
116         return;
117     }
118
119     // Otherwise we have to notify all those open connections and wait for them to close.
120     notifyConnectionsOfVersionChange();
121 }
122
123 bool UniqueIDBDatabase::hasAnyOpenConnections() const
124 {
125     return !m_openDatabaseConnections.isEmpty();
126 }
127
128 static uint64_t generateUniqueCallbackIdentifier()
129 {
130     ASSERT(isMainThread());
131     static uint64_t currentID = 0;
132     return ++currentID;
133 }
134
135 uint64_t UniqueIDBDatabase::storeCallback(ErrorCallback callback)
136 {
137     uint64_t identifier = generateUniqueCallbackIdentifier();
138     m_errorCallbacks.set(identifier, callback);
139     return identifier;
140 }
141
142 void UniqueIDBDatabase::startVersionChangeTransaction()
143 {
144     LOG(IndexedDB, "(main) UniqueIDBDatabase::startVersionChangeTransaction");
145
146     ASSERT(!m_versionChangeTransaction);
147     ASSERT(m_versionChangeOperation);
148     ASSERT(m_versionChangeDatabaseConnection);
149
150     auto operation = m_versionChangeOperation;
151     m_versionChangeOperation = nullptr;
152
153     uint64_t requestedVersion = operation->requestData().requestedVersion();
154     if (!requestedVersion)
155         requestedVersion = m_databaseInfo->version() ? m_databaseInfo->version() : 1;
156
157     addOpenDatabaseConnection(*m_versionChangeDatabaseConnection);
158
159     m_versionChangeTransaction = &m_versionChangeDatabaseConnection->createVersionChangeTransaction(requestedVersion);
160     m_server.postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::beginTransactionInBackingStore, m_versionChangeTransaction->info()));
161
162     auto result = IDBResultData::openDatabaseUpgradeNeeded(operation->requestData().requestIdentifier(), *m_versionChangeTransaction);
163     operation->connection().didOpenDatabase(result);
164 }
165
166 void UniqueIDBDatabase::beginTransactionInBackingStore(const IDBTransactionInfo& info)
167 {
168     LOG(IndexedDB, "(db) UniqueIDBDatabase::beginTransactionInBackingStore");
169     m_backingStore->beginTransaction(info);
170 }
171
172 void UniqueIDBDatabase::notifyConnectionsOfVersionChange()
173 {
174     ASSERT(m_versionChangeOperation);
175     ASSERT(m_versionChangeDatabaseConnection);
176
177     uint64_t requestedVersion = m_versionChangeOperation->requestData().requestedVersion();
178
179     LOG(IndexedDB, "(main) UniqueIDBDatabase::notifyConnectionsOfVersionChange - %" PRIu64, requestedVersion);
180
181     // 3.3.7 "versionchange" transaction steps
182     // Fire a versionchange event at each connection in m_openDatabaseConnections that is open.
183     // The event must not be fired on connections which has the closePending flag set.
184     for (auto connection : m_openDatabaseConnections) {
185         if (connection->closePending())
186             continue;
187
188         connection->fireVersionChangeEvent(requestedVersion);
189     }
190 }
191
192 void UniqueIDBDatabase::addOpenDatabaseConnection(Ref<UniqueIDBDatabaseConnection>&& connection)
193 {
194     ASSERT(!m_openDatabaseConnections.contains(&connection.get()));
195     m_openDatabaseConnections.add(adoptRef(connection.leakRef()));
196 }
197
198 void UniqueIDBDatabase::openBackingStore(const IDBDatabaseIdentifier& identifier)
199 {
200     ASSERT(!isMainThread());
201     LOG(IndexedDB, "(db) UniqueIDBDatabase::openBackingStore");
202
203     ASSERT(!m_backingStore);
204     m_backingStore = m_server.createBackingStore(identifier);
205     auto databaseInfo = m_backingStore->getOrEstablishDatabaseInfo();
206
207     m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didOpenBackingStore, databaseInfo));
208 }
209
210 void UniqueIDBDatabase::didOpenBackingStore(const IDBDatabaseInfo& info)
211 {
212     ASSERT(isMainThread());
213     LOG(IndexedDB, "(main) UniqueIDBDatabase::didOpenBackingStore");
214     
215     m_databaseInfo = std::make_unique<IDBDatabaseInfo>(info);
216
217     handleOpenDatabaseOperations();
218 }
219
220 void UniqueIDBDatabase::createObjectStore(UniqueIDBDatabaseTransaction& transaction, const IDBObjectStoreInfo& info, ErrorCallback callback)
221 {
222     ASSERT(isMainThread());
223     LOG(IndexedDB, "(main) UniqueIDBDatabase::createObjectStore");
224
225     uint64_t callbackID = storeCallback(callback);
226     m_server.postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performCreateObjectStore, callbackID, transaction.info().identifier(), info));
227 }
228
229 void UniqueIDBDatabase::performCreateObjectStore(uint64_t callbackIdentifier, const IDBResourceIdentifier&, const IDBObjectStoreInfo& info)
230 {
231     ASSERT(!isMainThread());
232     LOG(IndexedDB, "(db) UniqueIDBDatabase::performCreateObjectStore");
233
234     // FIXME: Create object store in backing store, once that exists.
235
236     IDBError error;
237     m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformCreateObjectStore, callbackIdentifier, error, info));
238 }
239
240 void UniqueIDBDatabase::didPerformCreateObjectStore(uint64_t callbackIdentifier, const IDBError& error, const IDBObjectStoreInfo& info)
241 {
242     ASSERT(isMainThread());
243     LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformCreateObjectStore");
244
245     if (error.isNull())
246         m_databaseInfo->addExistingObjectStore(info);
247
248     performErrorCallback(callbackIdentifier, error);
249 }
250
251 void UniqueIDBDatabase::commitTransaction(UniqueIDBDatabaseTransaction& transaction, ErrorCallback callback)
252 {
253     ASSERT(isMainThread());
254     LOG(IndexedDB, "(main) UniqueIDBDatabase::commitTransaction");
255
256     ASSERT(&transaction.databaseConnection().database() == this);
257
258     if (m_versionChangeTransaction == &transaction) {
259         ASSERT(&m_versionChangeTransaction->databaseConnection() == m_versionChangeDatabaseConnection);
260         m_databaseInfo->setVersion(transaction.info().newVersion());
261         m_versionChangeTransaction = nullptr;
262         m_versionChangeDatabaseConnection = nullptr;
263     }
264
265     uint64_t callbackID = storeCallback(callback);
266     m_server.postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performCommitTransaction, callbackID, transaction.info().identifier()));
267 }
268
269 void UniqueIDBDatabase::performCommitTransaction(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier)
270 {
271     ASSERT(!isMainThread());
272     LOG(IndexedDB, "(db) UniqueIDBDatabase::performCommitTransaction");
273
274     IDBError error = m_backingStore->commitTransaction(transactionIdentifier);
275     m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformCommitTransaction, callbackIdentifier, error));
276 }
277
278 void UniqueIDBDatabase::didPerformCommitTransaction(uint64_t callbackIdentifier, const IDBError& error)
279 {
280     ASSERT(isMainThread());
281     LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformCommitTransaction");
282
283     // Previously blocked transactions might now be unblocked.
284     invokeTransactionScheduler();
285
286     performErrorCallback(callbackIdentifier, error);
287 }
288
289 void UniqueIDBDatabase::abortTransaction(UniqueIDBDatabaseTransaction& transaction, ErrorCallback callback)
290 {
291     ASSERT(isMainThread());
292     LOG(IndexedDB, "(main) UniqueIDBDatabase::abortTransaction");
293
294     ASSERT(&transaction.databaseConnection().database() == this);
295
296     uint64_t callbackID = storeCallback(callback);
297     m_server.postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performAbortTransaction, callbackID, transaction.info().identifier()));
298 }
299
300 void UniqueIDBDatabase::performAbortTransaction(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier)
301 {
302     ASSERT(!isMainThread());
303     LOG(IndexedDB, "(db) UniqueIDBDatabase::performAbortTransaction");
304
305     IDBError error = m_backingStore->abortTransaction(transactionIdentifier);
306     m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformAbortTransaction, callbackIdentifier, error, transactionIdentifier));
307 }
308
309 void UniqueIDBDatabase::didPerformAbortTransaction(uint64_t callbackIdentifier, const IDBError& error, const IDBResourceIdentifier& transactionIdentifier)
310 {
311     ASSERT(isMainThread());
312     LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformAbortTransaction");
313
314     if (m_versionChangeTransaction || m_versionChangeTransaction->info().identifier() == transactionIdentifier) {
315         ASSERT(&m_versionChangeTransaction->databaseConnection() == m_versionChangeDatabaseConnection);
316         ASSERT(m_versionChangeTransaction->originalDatabaseInfo());
317         m_databaseInfo = std::make_unique<IDBDatabaseInfo>(*m_versionChangeTransaction->originalDatabaseInfo());
318
319         m_versionChangeTransaction = nullptr;
320         m_versionChangeDatabaseConnection = nullptr;
321     }
322
323     // Previously blocked transactions might now be unblocked.
324     invokeTransactionScheduler();
325
326     performErrorCallback(callbackIdentifier, error);
327 }
328
329 void UniqueIDBDatabase::transactionDestroyed(UniqueIDBDatabaseTransaction& transaction)
330 {
331     if (m_versionChangeTransaction == &transaction)
332         m_versionChangeTransaction = nullptr;
333 }
334
335 void UniqueIDBDatabase::connectionClosedFromClient(UniqueIDBDatabaseConnection& connection)
336 {
337     ASSERT(isMainThread());
338     LOG(IndexedDB, "(main) UniqueIDBDatabase::connectionClosedFromClient");
339
340     if (m_versionChangeDatabaseConnection == &connection)
341         m_versionChangeDatabaseConnection = nullptr;
342
343     ASSERT(m_openDatabaseConnections.contains(&connection));
344
345     auto removedConnection = m_openDatabaseConnections.take(&connection);
346     if (removedConnection->hasNonFinishedTransactions()) {
347         m_closePendingDatabaseConnections.add(WTF::move(removedConnection));
348         return;
349     }
350
351     // Now that a database connection has closed, previously blocked transactions might be runnable.
352     invokeTransactionScheduler();
353 }
354
355 void UniqueIDBDatabase::invokeTransactionScheduler()
356 {
357     if (!m_transactionSchedulingTimer.isActive())
358         m_transactionSchedulingTimer.startOneShot(0);
359 }
360
361 void UniqueIDBDatabase::transactionSchedulingTimerFired()
362 {
363     LOG(IndexedDB, "(main) UniqueIDBDatabase::transactionSchedulingTimerFired");
364
365     if (!hasAnyOpenConnections() && m_versionChangeOperation) {
366         startVersionChangeTransaction();
367         return;
368     }
369
370     // FIXME: Handle starting other pending transactions here.
371 }
372
373 void UniqueIDBDatabase::performErrorCallback(uint64_t callbackIdentifier, const IDBError& error)
374 {
375     auto callback = m_errorCallbacks.take(callbackIdentifier);
376     ASSERT(callback);
377     callback(error);
378 }
379
380 } // namespace IDBServer
381 } // namespace WebCore
382
383 #endif // ENABLE(INDEXED_DATABASE)