2 * Copyright (C) 2015 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "IDBDatabaseImpl.h"
29 #if ENABLE(INDEXED_DATABASE)
31 #include "EventQueue.h"
32 #include "IDBConnectionToServer.h"
33 #include "IDBDatabaseException.h"
34 #include "IDBOpenDBRequestImpl.h"
35 #include "IDBResultData.h"
36 #include "IDBTransactionImpl.h"
37 #include "IDBVersionChangeEventImpl.h"
39 #include "ScriptExecutionContext.h"
44 Ref<IDBDatabase> IDBDatabase::create(ScriptExecutionContext& context, IDBConnectionToServer& connection, const IDBResultData& resultData)
46 return adoptRef(*new IDBDatabase(context, connection, resultData));
49 IDBDatabase::IDBDatabase(ScriptExecutionContext& context, IDBConnectionToServer& connection, const IDBResultData& resultData)
50 : WebCore::IDBDatabase(&context)
51 , m_serverConnection(connection)
52 , m_info(resultData.databaseInfo())
53 , m_databaseConnectionIdentifier(resultData.databaseConnectionIdentifier())
56 relaxAdoptionRequirement();
57 m_serverConnection->registerDatabaseConnection(*this);
60 IDBDatabase::~IDBDatabase()
62 m_serverConnection->unregisterDatabaseConnection(*this);
65 bool IDBDatabase::hasPendingActivity() const
67 return !m_closedInServer;
70 const String IDBDatabase::name() const
75 uint64_t IDBDatabase::version() const
77 return m_info.version();
80 RefPtr<DOMStringList> IDBDatabase::objectStoreNames() const
82 RefPtr<DOMStringList> objectStoreNames = DOMStringList::create();
83 for (auto& name : m_info.objectStoreNames())
84 objectStoreNames->append(name);
85 objectStoreNames->sort();
86 return WTF::move(objectStoreNames);
89 RefPtr<WebCore::IDBObjectStore> IDBDatabase::createObjectStore(const String&, const Dictionary&, ExceptionCode&)
95 RefPtr<WebCore::IDBObjectStore> IDBDatabase::createObjectStore(const String& name, const IDBKeyPath& keyPath, bool autoIncrement, ExceptionCode& ec)
97 LOG(IndexedDB, "IDBDatabase::createObjectStore");
99 ASSERT(!m_versionChangeTransaction || m_versionChangeTransaction->isVersionChange());
101 if (!m_versionChangeTransaction) {
102 ec = IDBDatabaseException::InvalidStateError;
106 if (!m_versionChangeTransaction->isActive()) {
107 ec = IDBDatabaseException::TransactionInactiveError;
111 if (m_info.hasObjectStore(name)) {
112 ec = IDBDatabaseException::ConstraintError;
116 if (!keyPath.isNull() && !keyPath.isValid()) {
117 ec = IDBDatabaseException::SyntaxError;
121 if (autoIncrement && !keyPath.isNull()) {
122 if ((keyPath.type() == IndexedDB::KeyPathType::String && keyPath.string().isEmpty()) || keyPath.type() == IndexedDB::KeyPathType::Array) {
123 ec = IDBDatabaseException::InvalidAccessError;
128 // Install the new ObjectStore into the connection's metadata.
129 IDBObjectStoreInfo info = m_info.createNewObjectStore(name, keyPath, autoIncrement);
131 // Create the actual IDBObjectStore from the transaction, which also schedules the operation server side.
132 Ref<IDBObjectStore> objectStore = m_versionChangeTransaction->createObjectStore(info);
133 return adoptRef(&objectStore.leakRef());
136 RefPtr<WebCore::IDBTransaction> IDBDatabase::transaction(ScriptExecutionContext*, const Vector<String>& objectStores, const String& modeString, ExceptionCode& ec)
138 LOG(IndexedDB, "IDBDatabase::transaction");
140 if (m_closePending) {
141 ec = INVALID_STATE_ERR;
145 if (objectStores.isEmpty()) {
146 ec = INVALID_ACCESS_ERR;
150 IndexedDB::TransactionMode mode = IDBTransaction::stringToMode(modeString, ec);
154 if (mode != IndexedDB::TransactionMode::ReadOnly && mode != IndexedDB::TransactionMode::ReadWrite) {
159 if (m_versionChangeTransaction && !m_versionChangeTransaction->isFinishedOrFinishing()) {
160 ec = INVALID_STATE_ERR;
164 for (auto& objectStoreName : objectStores) {
165 if (m_info.hasObjectStore(objectStoreName))
171 auto info = IDBTransactionInfo::clientTransaction(m_serverConnection.get(), objectStores, mode);
172 auto transaction = IDBTransaction::create(*this, info);
174 LOG(IndexedDB, "IDBDatabase::transaction - Added active transaction %s", info.identifier().loggingString().utf8().data());
176 m_activeTransactions.set(info.identifier(), &transaction.get());
178 return adoptRef(&transaction.leakRef());
181 RefPtr<WebCore::IDBTransaction> IDBDatabase::transaction(ScriptExecutionContext* context, const String& objectStore, const String& mode, ExceptionCode& ec)
183 Vector<String> objectStores(1);
184 objectStores[0] = objectStore;
185 return transaction(context, objectStores, mode, ec);
188 void IDBDatabase::deleteObjectStore(const String& objectStoreName, ExceptionCode& ec)
190 LOG(IndexedDB, "IDBDatabase::deleteObjectStore");
192 if (!m_versionChangeTransaction) {
193 ec = INVALID_STATE_ERR;
197 if (!m_versionChangeTransaction->isActive()) {
198 ec = IDBDatabaseException::TransactionInactiveError;
202 if (!m_info.hasObjectStore(objectStoreName)) {
203 ec = IDBDatabaseException::NotFoundError;
207 m_info.deleteObjectStore(objectStoreName);
208 m_versionChangeTransaction->deleteObjectStore(objectStoreName);
211 void IDBDatabase::close()
213 m_closePending = true;
214 maybeCloseInServer();
217 void IDBDatabase::maybeCloseInServer()
219 if (m_closedInServer)
222 // 3.3.9 Database closing steps
223 // Wait for all transactions created using this connection to complete.
224 // Once they are complete, this connection is closed.
225 if (!m_activeTransactions.isEmpty() || !m_committingTransactions.isEmpty())
228 m_closedInServer = true;
229 m_serverConnection->databaseConnectionClosed(*this);
232 const char* IDBDatabase::activeDOMObjectName() const
234 return "IDBDatabase";
237 bool IDBDatabase::canSuspendForPageCache() const
239 // FIXME: This value will sometimes be false when database operations are actually in progress.
240 // Such database operations do not yet exist.
244 Ref<IDBTransaction> IDBDatabase::startVersionChangeTransaction(const IDBTransactionInfo& info, IDBOpenDBRequest& request)
246 LOG(IndexedDB, "IDBDatabase::startVersionChangeTransaction %s", info.identifier().loggingString().utf8().data());
248 ASSERT(!m_versionChangeTransaction);
249 ASSERT(info.mode() == IndexedDB::TransactionMode::VersionChange);
251 Ref<IDBTransaction> transaction = IDBTransaction::create(*this, info, request);
252 m_versionChangeTransaction = &transaction.get();
254 m_activeTransactions.set(transaction->info().identifier(), &transaction.get());
256 return WTF::move(transaction);
259 void IDBDatabase::didStartTransaction(IDBTransaction& transaction)
261 LOG(IndexedDB, "IDBDatabase::didStartTransaction %s", transaction.info().identifier().loggingString().utf8().data());
262 ASSERT(!m_versionChangeTransaction);
264 // It is possible for the client to have aborted a transaction before the server replies back that it has started.
265 if (m_abortingTransactions.contains(transaction.info().identifier()))
268 m_activeTransactions.set(transaction.info().identifier(), &transaction);
271 void IDBDatabase::willCommitTransaction(IDBTransaction& transaction)
273 LOG(IndexedDB, "IDBDatabase::willCommitTransaction %s", transaction.info().identifier().loggingString().utf8().data());
275 auto refTransaction = m_activeTransactions.take(transaction.info().identifier());
276 ASSERT(refTransaction);
277 m_committingTransactions.set(transaction.info().identifier(), WTF::move(refTransaction));
280 void IDBDatabase::didCommitTransaction(IDBTransaction& transaction)
282 LOG(IndexedDB, "IDBDatabase::didCommitTransaction %s", transaction.info().identifier().loggingString().utf8().data());
284 if (m_versionChangeTransaction == &transaction)
285 m_info.setVersion(transaction.info().newVersion());
287 didCommitOrAbortTransaction(transaction);
290 void IDBDatabase::willAbortTransaction(IDBTransaction& transaction)
292 LOG(IndexedDB, "IDBDatabase::willAbortTransaction %s", transaction.info().identifier().loggingString().utf8().data());
294 auto refTransaction = m_activeTransactions.take(transaction.info().identifier());
295 ASSERT(refTransaction);
296 m_abortingTransactions.set(transaction.info().identifier(), WTF::move(refTransaction));
299 void IDBDatabase::didAbortTransaction(IDBTransaction& transaction)
301 LOG(IndexedDB, "IDBDatabase::didAbortTransaction %s", transaction.info().identifier().loggingString().utf8().data());
303 if (transaction.isVersionChange()) {
304 ASSERT(transaction.originalDatabaseInfo());
305 m_info = *transaction.originalDatabaseInfo();
308 didCommitOrAbortTransaction(transaction);
311 void IDBDatabase::didCommitOrAbortTransaction(IDBTransaction& transaction)
313 LOG(IndexedDB, "IDBDatabase::didCommitOrAbortTransaction %s", transaction.info().identifier().loggingString().utf8().data());
315 if (m_versionChangeTransaction == &transaction)
316 m_versionChangeTransaction = nullptr;
320 if (m_activeTransactions.contains(transaction.info().identifier()))
322 if (m_committingTransactions.contains(transaction.info().identifier()))
324 if (m_abortingTransactions.contains(transaction.info().identifier()))
330 m_activeTransactions.remove(transaction.info().identifier());
331 m_committingTransactions.remove(transaction.info().identifier());
332 m_abortingTransactions.remove(transaction.info().identifier());
335 maybeCloseInServer();
338 void IDBDatabase::fireVersionChangeEvent(uint64_t requestedVersion)
340 uint64_t currentVersion = m_info.version();
341 LOG(IndexedDB, "IDBDatabase::fireVersionChangeEvent - current version %" PRIu64 ", requested version %" PRIu64, currentVersion, requestedVersion);
343 if (!scriptExecutionContext())
346 Ref<Event> event = IDBVersionChangeEvent::create(currentVersion, requestedVersion, eventNames().versionchangeEvent);
347 event->setTarget(this);
348 scriptExecutionContext()->eventQueue().enqueueEvent(WTF::move(event));
351 void IDBDatabase::didCreateIndexInfo(const IDBIndexInfo& info)
353 auto* objectStore = m_info.infoForExistingObjectStore(info.objectStoreIdentifier());
355 objectStore->addExistingIndex(info);
358 void IDBDatabase::didDeleteIndexInfo(const IDBIndexInfo& info)
360 auto* objectStore = m_info.infoForExistingObjectStore(info.objectStoreIdentifier());
362 objectStore->deleteIndex(info.name());
365 } // namespace IDBClient
366 } // namespace WebCore
368 #endif // ENABLE(INDEXED_DATABASE)