Modern IDB: Basic createObjectStore implementation.
[WebKit-https.git] / Source / WebCore / Modules / indexeddb / client / IDBTransactionImpl.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 "IDBTransactionImpl.h"
28
29 #if ENABLE(INDEXED_DATABASE)
30
31 #include "DOMError.h"
32 #include "EventQueue.h"
33 #include "IDBDatabaseImpl.h"
34 #include "IDBError.h"
35 #include "IDBEventDispatcher.h"
36 #include "IDBObjectStore.h"
37 #include "IDBResultData.h"
38 #include "Logging.h"
39 #include "ScriptExecutionContext.h"
40 #include "TransactionOperation.h"
41
42 namespace WebCore {
43 namespace IDBClient {
44
45 Ref<IDBTransaction> IDBTransaction::create(IDBDatabase& database, const IDBTransactionInfo& info)
46 {
47     return adoptRef(*new IDBTransaction(database, info));
48 }
49
50 IDBTransaction::IDBTransaction(IDBDatabase& database, const IDBTransactionInfo& info)
51     : WebCore::IDBTransaction(database.scriptExecutionContext())
52     , m_database(database)
53     , m_info(info)
54     , m_operationTimer(*this, &IDBTransaction::operationTimerFired)
55
56 {
57     m_activationTimer = std::make_unique<Timer>(*this, &IDBTransaction::activationTimerFired);
58     m_activationTimer->startOneShot(0);
59
60     if (m_info.mode() == IndexedDB::TransactionMode::VersionChange)
61         m_originalDatabaseInfo = std::make_unique<IDBDatabaseInfo>(m_database->info());
62
63     suspendIfNeeded();
64     m_state = IndexedDB::TransactionState::Running;
65 }
66
67 IDBTransaction::~IDBTransaction()
68 {
69 }
70
71 const String& IDBTransaction::mode() const
72 {
73     switch (m_info.mode()) {
74     case IndexedDB::TransactionMode::ReadOnly:
75         return IDBTransaction::modeReadOnly();
76     case IndexedDB::TransactionMode::ReadWrite:
77         return IDBTransaction::modeReadWrite();
78     case IndexedDB::TransactionMode::VersionChange:
79         return IDBTransaction::modeVersionChange();
80     }
81
82     RELEASE_ASSERT_NOT_REACHED();
83 }
84
85 WebCore::IDBDatabase* IDBTransaction::db()
86 {
87     return &m_database.get();
88 }
89
90 IDBConnectionToServer& IDBTransaction::serverConnection()
91 {
92     return m_database->serverConnection();
93 }
94
95 RefPtr<DOMError> IDBTransaction::error() const
96 {
97     ASSERT_NOT_REACHED();
98     return nullptr;
99 }
100
101 RefPtr<WebCore::IDBObjectStore> IDBTransaction::objectStore(const String&, ExceptionCode&)
102 {
103     ASSERT_NOT_REACHED();
104     return nullptr;
105 }
106
107 void IDBTransaction::abort(ExceptionCode& ec)
108 {
109     LOG(IndexedDB, "IDBTransaction::abort");
110
111     if (isFinishedOrFinishing()) {
112         ec = INVALID_STATE_ERR;
113         return;
114     }
115
116     m_state = IndexedDB::TransactionState::Aborting;
117
118     m_database->abortTransaction(*this);
119 }
120
121 const char* IDBTransaction::activeDOMObjectName() const
122 {
123     return "IDBTransaction";
124 }
125
126 bool IDBTransaction::canSuspendForPageCache() const
127 {
128     return false;
129 }
130
131 bool IDBTransaction::hasPendingActivity() const
132 {
133     return m_state != IndexedDB::TransactionState::Finished;
134 }
135
136 bool IDBTransaction::isActive() const
137 {
138     return m_state == IndexedDB::TransactionState::Running;
139 }
140
141 bool IDBTransaction::isFinishedOrFinishing() const
142 {
143     return m_state == IndexedDB::TransactionState::Committing
144         || m_state == IndexedDB::TransactionState::Aborting
145         || m_state == IndexedDB::TransactionState::Finished;
146 }
147
148 void IDBTransaction::activationTimerFired()
149 {
150     scheduleOperationTimer();
151     m_activationTimer = nullptr;
152 }
153
154 void IDBTransaction::scheduleOperation(RefPtr<TransactionOperation>&& operation)
155 {
156     ASSERT(!m_transactionOperationMap.contains(operation->identifier()));
157
158     m_transactionOperationQueue.append(operation);
159     m_transactionOperationMap.set(operation->identifier(), WTF::move(operation));
160
161     scheduleOperationTimer();
162 }
163
164 void IDBTransaction::scheduleOperationTimer()
165 {
166     if (!m_operationTimer.isActive())
167         m_operationTimer.startOneShot(0);
168 }
169
170 void IDBTransaction::operationTimerFired()
171 {
172     LOG(IndexedDB, "IDBTransaction::operationTimerFired");
173
174     if (m_state == IndexedDB::TransactionState::Unstarted)
175         return;
176
177     if (!m_transactionOperationQueue.isEmpty()) {
178         auto operation = m_transactionOperationQueue.takeFirst();
179         operation->perform();
180
181         return;
182     }
183
184     if (isActive())
185         commit();
186 }
187
188 void IDBTransaction::commit()
189 {
190     LOG(IndexedDB, "IDBTransaction::commit");
191
192     if (m_state != IndexedDB::TransactionState::Running) {
193         m_state = IndexedDB::TransactionState::Finished;
194         return;
195     }
196
197     m_state = IndexedDB::TransactionState::Committing;
198
199     m_database->commitTransaction(*this);
200 }
201
202 void IDBTransaction::finishAbortOrCommit()
203 {
204     ASSERT(m_state != IndexedDB::TransactionState::Finished);
205     m_state = IndexedDB::TransactionState::Finished;
206
207     m_originalDatabaseInfo = nullptr;
208 }
209
210 void IDBTransaction::didAbort(const IDBError& error)
211 {
212     LOG(IndexedDB, "IDBTransaction::didAbort");
213
214     ASSERT(m_state == IndexedDB::TransactionState::Aborting || m_state == IndexedDB::TransactionState::Running);
215
216     m_database->didAbortTransaction(*this);
217
218     m_idbError = error;
219     fireOnAbort();
220
221     finishAbortOrCommit();
222 }
223
224 void IDBTransaction::didCommit(const IDBError& error)
225 {
226     LOG(IndexedDB, "IDBTransaction::didCommit");
227
228     ASSERT(m_state == IndexedDB::TransactionState::Committing);
229
230     if (error.isNull()) {
231         m_database->didCommitTransaction(*this);
232         fireOnComplete();
233     } else {
234         m_database->didAbortTransaction(*this);
235         m_idbError = error;
236         fireOnAbort();
237     }
238
239     finishAbortOrCommit();
240 }
241
242 void IDBTransaction::fireOnComplete()
243 {
244     LOG(IndexedDB, "IDBTransaction::fireOnComplete");
245     enqueueEvent(Event::create(eventNames().completeEvent, false, false));
246 }
247
248 void IDBTransaction::fireOnAbort()
249 {
250     LOG(IndexedDB, "IDBTransaction::fireOnAbort");
251     enqueueEvent(Event::create(eventNames().abortEvent, true, false));
252 }
253
254 void IDBTransaction::enqueueEvent(Ref<Event> event)
255 {
256     ASSERT(m_state != IndexedDB::TransactionState::Finished);
257
258     if (!scriptExecutionContext())
259         return;
260
261     event->setTarget(this);
262     scriptExecutionContext()->eventQueue().enqueueEvent(&event.get());
263 }
264
265 bool IDBTransaction::dispatchEvent(PassRefPtr<Event> event)
266 {
267     LOG(IndexedDB, "IDBTransaction::dispatchEvent");
268
269     ASSERT(scriptExecutionContext());
270     ASSERT(event->target() == this);
271     ASSERT(event->type() == eventNames().completeEvent || event->type() == eventNames().abortEvent);
272
273     Vector<RefPtr<EventTarget>> targets;
274     targets.append(this);
275     targets.append(db());
276
277     return IDBEventDispatcher::dispatch(event.get(), targets);
278 }
279
280 Ref<IDBObjectStore> IDBTransaction::createObjectStore(const IDBObjectStoreInfo& info)
281 {
282     LOG(IndexedDB, "IDBTransaction::createObjectStore");
283     ASSERT(isVersionChange());
284
285     Ref<IDBObjectStore> objectStore = IDBObjectStore::create(info, *this);
286
287     auto operation = createTransactionOperation(*this, &IDBTransaction::didCreateObjectStoreOnServer, &IDBTransaction::createObjectStoreOnServer, info);
288     scheduleOperation(WTF::move(operation));
289
290     return WTF::move(objectStore);
291 }
292
293 void IDBTransaction::createObjectStoreOnServer(TransactionOperation& operation, const IDBObjectStoreInfo& info)
294 {
295     LOG(IndexedDB, "IDBTransaction::createObjectStoreOnServer");
296
297     ASSERT(isActive());
298     ASSERT(isVersionChange());
299
300     m_database->serverConnection().createObjectStore(operation, info);
301 }
302
303 void IDBTransaction::didCreateObjectStoreOnServer(const IDBResultData& resultData)
304 {
305     LOG(IndexedDB, "IDBTransaction::didCreateObjectStoreOnServer");
306
307     ASSERT_UNUSED(resultData, resultData.type() == IDBResultType::CreateObjectStoreSuccess);
308
309     scheduleOperationTimer();
310 }
311
312 } // namespace IDBClient
313 } // namespace WebCore
314
315 #endif // ENABLE(INDEXED_DATABASE)