9ff1c8e0bf02dbc5af653a9127f52ee72c4ec056
[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     m_server.registerDatabaseConnection(*rawConnection);
96
97     if (requestedVersion == m_databaseInfo->version()) {
98         addOpenDatabaseConnection(WTF::move(connection));
99
100         auto result = IDBResultData::openDatabaseSuccess(operation->requestData().requestIdentifier(), *rawConnection);
101         operation->connection().didOpenDatabase(result);
102         return;
103     }
104
105     ASSERT(!m_versionChangeOperation);
106     ASSERT(!m_versionChangeDatabaseConnection);
107
108     m_versionChangeOperation = adoptRef(operation.leakRef());
109     m_versionChangeDatabaseConnection = rawConnection;
110
111     // 3.3.7 "versionchange" transaction steps
112     // If there's no other open connections to this database, the version change process can begin immediately.
113     if (!hasAnyOpenConnections()) {
114         startVersionChangeTransaction();
115         return;
116     }
117
118     // Otherwise we have to notify all those open connections and wait for them to close.
119     notifyConnectionsOfVersionChange();
120 }
121
122 bool UniqueIDBDatabase::hasAnyOpenConnections() const
123 {
124     return !m_openDatabaseConnections.isEmpty();
125 }
126
127 static uint64_t generateUniqueCallbackIdentifier()
128 {
129     ASSERT(isMainThread());
130     static uint64_t currentID = 0;
131     return ++currentID;
132 }
133
134 uint64_t UniqueIDBDatabase::storeCallback(ErrorCallback callback)
135 {
136     uint64_t identifier = generateUniqueCallbackIdentifier();
137     m_errorCallbacks.set(identifier, callback);
138     return identifier;
139 }
140
141 void UniqueIDBDatabase::startVersionChangeTransaction()
142 {
143     LOG(IndexedDB, "(main) UniqueIDBDatabase::startVersionChangeTransaction");
144
145     ASSERT(!m_versionChangeTransaction);
146     ASSERT(m_versionChangeOperation);
147     ASSERT(m_versionChangeDatabaseConnection);
148
149     auto operation = m_versionChangeOperation;
150     m_versionChangeOperation = nullptr;
151
152     uint64_t requestedVersion = operation->requestData().requestedVersion();
153     if (!requestedVersion)
154         requestedVersion = m_databaseInfo->version() ? m_databaseInfo->version() : 1;
155
156     addOpenDatabaseConnection(*m_versionChangeDatabaseConnection);
157
158     m_versionChangeTransaction = &m_versionChangeDatabaseConnection->createVersionChangeTransaction(requestedVersion);
159
160     auto result = IDBResultData::openDatabaseUpgradeNeeded(operation->requestData().requestIdentifier(), *m_versionChangeTransaction);
161     operation->connection().didOpenDatabase(result);
162 }
163
164 void UniqueIDBDatabase::notifyConnectionsOfVersionChange()
165 {
166     ASSERT(m_versionChangeOperation);
167     ASSERT(m_versionChangeDatabaseConnection);
168
169     uint64_t requestedVersion = m_versionChangeOperation->requestData().requestedVersion();
170
171     LOG(IndexedDB, "(main) UniqueIDBDatabase::notifyConnectionsOfVersionChange - %llu", static_cast<unsigned long long>(requestedVersion));
172
173     // 3.3.7 "versionchange" transaction steps
174     // Fire a versionchange event at each connection in m_openDatabaseConnections that is open.
175     // The event must not be fired on connections which has the closePending flag set.
176     for (auto connection : m_openDatabaseConnections) {
177         if (connection->closePending())
178             continue;
179
180         connection->fireVersionChangeEvent(requestedVersion);
181     }
182 }
183
184 void UniqueIDBDatabase::addOpenDatabaseConnection(Ref<UniqueIDBDatabaseConnection>&& connection)
185 {
186     ASSERT(!m_openDatabaseConnections.contains(&connection.get()));
187     m_openDatabaseConnections.add(adoptRef(connection.leakRef()));
188 }
189
190 void UniqueIDBDatabase::openBackingStore(const IDBDatabaseIdentifier& identifier)
191 {
192     ASSERT(!isMainThread());
193     LOG(IndexedDB, "(db) UniqueIDBDatabase::openBackingStore");
194
195     ASSERT(!m_backingStore);
196     m_backingStore = m_server.createBackingStore(identifier);
197     auto databaseInfo = m_backingStore->getOrEstablishDatabaseInfo();
198
199     m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didOpenBackingStore, databaseInfo));
200 }
201
202 void UniqueIDBDatabase::didOpenBackingStore(const IDBDatabaseInfo& info)
203 {
204     ASSERT(isMainThread());
205     LOG(IndexedDB, "(main) UniqueIDBDatabase::didOpenBackingStore");
206     
207     m_databaseInfo = std::make_unique<IDBDatabaseInfo>(info);
208
209     handleOpenDatabaseOperations();
210 }
211
212 void UniqueIDBDatabase::commitTransaction(UniqueIDBDatabaseTransaction& transaction, ErrorCallback callback)
213 {
214     ASSERT(isMainThread());
215     LOG(IndexedDB, "(main) UniqueIDBDatabase::commitTransaction");
216
217     if (m_versionChangeTransaction == &transaction) {
218         ASSERT(&m_versionChangeTransaction->databaseConnection() == m_versionChangeDatabaseConnection);
219         m_databaseInfo->setVersion(transaction.info().newVersion());
220         m_versionChangeTransaction = nullptr;
221         m_versionChangeDatabaseConnection = nullptr;
222     }
223
224     uint64_t callbackID = storeCallback(callback);
225     m_server.postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performCommitTransaction, callbackID, transaction.info().identifier()));
226 }
227
228 void UniqueIDBDatabase::performCommitTransaction(uint64_t callbackIdentifier, const IDBResourceIdentifier&)
229 {
230     ASSERT(!isMainThread());
231     LOG(IndexedDB, "(db) UniqueIDBDatabase::performCommitTransaction");
232
233     // FIXME: Commit transaction in backing store, once that exists.
234
235     IDBError error;
236     m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformCommitTransaction, callbackIdentifier, error));
237
238 }
239
240 void UniqueIDBDatabase::didPerformCommitTransaction(uint64_t callbackIdentifier, const IDBError& error)
241 {
242     ASSERT(isMainThread());
243     LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformCommitTransaction");
244
245     performErrorCallback(callbackIdentifier, error);
246 }
247
248 void UniqueIDBDatabase::transactionDestroyed(UniqueIDBDatabaseTransaction& transaction)
249 {
250     if (m_versionChangeTransaction == &transaction)
251         m_versionChangeTransaction = nullptr;
252 }
253
254 void UniqueIDBDatabase::performErrorCallback(uint64_t callbackIdentifier, const IDBError& error)
255 {
256     auto callback = m_errorCallbacks.take(callbackIdentifier);
257     ASSERT(callback);
258     callback(error);
259 }
260
261 } // namespace IDBServer
262 } // namespace WebCore
263
264 #endif // ENABLE(INDEXED_DATABASE)