541e5d8d90081f97ea1f77a870e67c0bf58a373e
[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 "Logging.h"
38 #include "ScriptExecutionContext.h"
39
40 namespace WebCore {
41 namespace IDBClient {
42
43 Ref<IDBTransaction> IDBTransaction::create(IDBDatabase& database, const IDBTransactionInfo& info)
44 {
45     return adoptRef(*new IDBTransaction(database, info));
46 }
47
48 IDBTransaction::IDBTransaction(IDBDatabase& database, const IDBTransactionInfo& info)
49     : WebCore::IDBTransaction(database.scriptExecutionContext())
50     , m_database(database)
51     , m_info(info)
52     , m_operationTimer(*this, &IDBTransaction::operationTimerFired)
53
54 {
55     m_activationTimer = std::make_unique<Timer>(*this, &IDBTransaction::activationTimerFired);
56     m_activationTimer->startOneShot(0);
57
58     if (m_info.mode() == IndexedDB::TransactionMode::VersionChange)
59         m_originalDatabaseInfo = std::make_unique<IDBDatabaseInfo>(m_database->info());
60
61     suspendIfNeeded();
62     m_state = IndexedDB::TransactionState::Running;
63 }
64
65 IDBTransaction::~IDBTransaction()
66 {
67 }
68
69 const String& IDBTransaction::mode() const
70 {
71     switch (m_info.mode()) {
72     case IndexedDB::TransactionMode::ReadOnly:
73         return IDBTransaction::modeReadOnly();
74     case IndexedDB::TransactionMode::ReadWrite:
75         return IDBTransaction::modeReadWrite();
76     case IndexedDB::TransactionMode::VersionChange:
77         return IDBTransaction::modeVersionChange();
78     }
79
80     RELEASE_ASSERT_NOT_REACHED();
81 }
82
83 WebCore::IDBDatabase* IDBTransaction::db()
84 {
85     return &m_database.get();
86 }
87
88 RefPtr<DOMError> IDBTransaction::error() const
89 {
90     ASSERT_NOT_REACHED();
91     return nullptr;
92 }
93
94 RefPtr<IDBObjectStore> IDBTransaction::objectStore(const String&, ExceptionCode&)
95 {
96     ASSERT_NOT_REACHED();
97     return nullptr;
98 }
99
100 void IDBTransaction::abort(ExceptionCode& ec)
101 {
102     LOG(IndexedDB, "IDBTransaction::abort");
103
104     if (isFinishedOrFinishing()) {
105         ec = INVALID_STATE_ERR;
106         return;
107     }
108
109     m_state = IndexedDB::TransactionState::Aborting;
110
111     m_database->abortTransaction(*this);
112 }
113
114 const char* IDBTransaction::activeDOMObjectName() const
115 {
116     return "IDBTransaction";
117 }
118
119 bool IDBTransaction::canSuspendForPageCache() const
120 {
121     return false;
122 }
123
124 bool IDBTransaction::hasPendingActivity() const
125 {
126     return m_state != IndexedDB::TransactionState::Finished;
127 }
128
129 bool IDBTransaction::isActive() const
130 {
131     return m_state == IndexedDB::TransactionState::Running;
132 }
133
134 bool IDBTransaction::isFinishedOrFinishing() const
135 {
136     return m_state == IndexedDB::TransactionState::Committing
137         || m_state == IndexedDB::TransactionState::Aborting
138         || m_state == IndexedDB::TransactionState::Finished;
139 }
140
141 void IDBTransaction::activationTimerFired()
142 {
143     scheduleOperationTimer();
144     m_activationTimer = nullptr;
145 }
146
147 void IDBTransaction::scheduleOperationTimer()
148 {
149     if (!m_operationTimer.isActive())
150         m_operationTimer.startOneShot(0);
151 }
152
153 void IDBTransaction::operationTimerFired()
154 {
155     LOG(IndexedDB, "IDBTransaction::operationTimerFired");
156
157     if (m_state == IndexedDB::TransactionState::Unstarted)
158         return;
159
160     // FIXME: Once transactions can do things, like configure the database or insert data into it,
161     // those operations will be handled here, and will prevent the transaction from committing
162     // as long as outstanding operations exist.
163
164     if (isActive())
165         commit();
166 }
167
168 void IDBTransaction::commit()
169 {
170     LOG(IndexedDB, "IDBTransaction::commit");
171
172     if (m_state != IndexedDB::TransactionState::Running) {
173         m_state = IndexedDB::TransactionState::Finished;
174         return;
175     }
176
177     m_state = IndexedDB::TransactionState::Committing;
178
179     m_database->commitTransaction(*this);
180 }
181
182 void IDBTransaction::didAbort(const IDBError& error)
183 {
184     LOG(IndexedDB, "IDBTransaction::didAbort");
185
186     ASSERT(m_state == IndexedDB::TransactionState::Aborting || m_state == IndexedDB::TransactionState::Running);
187
188     m_database->didAbortTransaction(*this);
189
190     m_idbError = error;
191     fireOnAbort();
192
193     m_state = IndexedDB::TransactionState::Finished;
194 }
195
196 void IDBTransaction::didCommit(const IDBError& error)
197 {
198     LOG(IndexedDB, "IDBTransaction::didCommit");
199
200     ASSERT(m_state == IndexedDB::TransactionState::Committing);
201
202     if (error.isNull()) {
203         m_database->didCommitTransaction(*this);
204         fireOnComplete();
205     } else {
206         m_database->didAbortTransaction(*this);
207         m_idbError = error;
208         fireOnAbort();
209     }
210
211     m_state = IndexedDB::TransactionState::Finished;
212 }
213
214 void IDBTransaction::fireOnComplete()
215 {
216     LOG(IndexedDB, "IDBTransaction::fireOnComplete");
217     enqueueEvent(Event::create(eventNames().completeEvent, false, false));
218 }
219
220 void IDBTransaction::fireOnAbort()
221 {
222     LOG(IndexedDB, "IDBTransaction::fireOnAbort");
223     enqueueEvent(Event::create(eventNames().abortEvent, true, false));
224 }
225
226 void IDBTransaction::enqueueEvent(Ref<Event> event)
227 {
228     ASSERT(m_state != IndexedDB::TransactionState::Finished);
229
230     if (!scriptExecutionContext())
231         return;
232
233     event->setTarget(this);
234     scriptExecutionContext()->eventQueue().enqueueEvent(&event.get());
235 }
236
237 bool IDBTransaction::dispatchEvent(PassRefPtr<Event> event)
238 {
239     LOG(IndexedDB, "IDBTransaction::dispatchEvent");
240
241     ASSERT(scriptExecutionContext());
242     ASSERT(event->target() == this);
243     ASSERT(event->type() == eventNames().completeEvent || event->type() == eventNames().abortEvent);
244
245     Vector<RefPtr<EventTarget>> targets;
246     targets.append(this);
247     targets.append(db());
248
249     return IDBEventDispatcher::dispatch(event.get(), targets);
250 }
251
252 } // namespace IDBClient
253 } // namespace WebCore
254
255 #endif // ENABLE(INDEXED_DATABASE)