1/*
2 * Copyright (C) 2015, 2016 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 "IDBOpenDBRequest.h"
28
29#if ENABLE(INDEXED_DATABASE)
30
31#include "DOMException.h"
32#include "EventNames.h"
33#include "IDBConnectionProxy.h"
34#include "IDBConnectionToServer.h"
35#include "IDBDatabase.h"
36#include "IDBError.h"
37#include "IDBRequestCompletionEvent.h"
38#include "IDBResultData.h"
39#include "IDBTransaction.h"
40#include "IDBVersionChangeEvent.h"
41#include "Logging.h"
42#include "ScriptExecutionContext.h"
43#include <wtf/IsoMallocInlines.h>
44
45namespace WebCore {
46
47WTF_MAKE_ISO_ALLOCATED_IMPL(IDBOpenDBRequest);
48
49Ref<IDBOpenDBRequest> IDBOpenDBRequest::createDeleteRequest(ScriptExecutionContext& context, IDBClient::IDBConnectionProxy& connectionProxy, const IDBDatabaseIdentifier& databaseIdentifier)
50{
51 return adoptRef(*new IDBOpenDBRequest(context, connectionProxy, databaseIdentifier, 0, IndexedDB::RequestType::Delete));
52}
53
54Ref<IDBOpenDBRequest> IDBOpenDBRequest::createOpenRequest(ScriptExecutionContext& context, IDBClient::IDBConnectionProxy& connectionProxy, const IDBDatabaseIdentifier& databaseIdentifier, uint64_t version)
55{
56 return adoptRef(*new IDBOpenDBRequest(context, connectionProxy, databaseIdentifier, version, IndexedDB::RequestType::Open));
57}
58
59IDBOpenDBRequest::IDBOpenDBRequest(ScriptExecutionContext& context, IDBClient::IDBConnectionProxy& connectionProxy, const IDBDatabaseIdentifier& databaseIdentifier, uint64_t version, IndexedDB::RequestType requestType)
60 : IDBRequest(context, connectionProxy)
61 , m_databaseIdentifier(databaseIdentifier)
62 , m_version(version)
63{
64 m_requestType = requestType;
65}
66
67IDBOpenDBRequest::~IDBOpenDBRequest()
68{
69 ASSERT(&originThread() == &Thread::current());
70}
71
72void IDBOpenDBRequest::onError(const IDBResultData& data)
73{
74 ASSERT(&originThread() == &Thread::current());
75
76 m_domError = data.error().toDOMException();
77 enqueueEvent(IDBRequestCompletionEvent::create(eventNames().errorEvent, Event::CanBubble::Yes, Event::IsCancelable::Yes, *this));
78}
79
80void IDBOpenDBRequest::versionChangeTransactionDidFinish()
81{
82 ASSERT(&originThread() == &Thread::current());
83
84 // 3.3.7 "versionchange" transaction steps
85 // When the transaction is finished, after firing complete/abort on the transaction, immediately set request's transaction property to null.
86 m_shouldExposeTransactionToDOM = false;
87}
88
89void IDBOpenDBRequest::fireSuccessAfterVersionChangeCommit()
90{
91 LOG(IndexedDB, "IDBOpenDBRequest::fireSuccessAfterVersionChangeCommit() - %s", resourceIdentifier().loggingString().utf8().data());
92
93 ASSERT(&originThread() == &Thread::current());
94 ASSERT(hasPendingActivity());
95 m_transaction->addRequest(*this);
96
97 auto event = IDBRequestCompletionEvent::create(eventNames().successEvent, Event::CanBubble::No, Event::IsCancelable::No, *this);
98 m_openDatabaseSuccessEvent = &event.get();
99
100 enqueueEvent(WTFMove(event));
101}
102
103void IDBOpenDBRequest::fireErrorAfterVersionChangeCompletion()
104{
105 LOG(IndexedDB, "IDBOpenDBRequest::fireErrorAfterVersionChangeCompletion() - %s", resourceIdentifier().loggingString().utf8().data());
106
107 ASSERT(&originThread() == &Thread::current());
108 ASSERT(hasPendingActivity());
109
110 IDBError idbError(AbortError);
111 m_domError = DOMException::create(AbortError);
112 setResultToUndefined();
113
114 m_transaction->addRequest(*this);
115 enqueueEvent(IDBRequestCompletionEvent::create(eventNames().errorEvent, Event::CanBubble::Yes, Event::IsCancelable::Yes, *this));
116}
117
118void IDBOpenDBRequest::cancelForStop()
119{
120 connectionProxy().openDBRequestCancelled({ connectionProxy(), *this });
121
122 if (m_transaction && m_transaction->isVersionChange())
123 m_transaction->removeRequest(*this);
124}
125
126void IDBOpenDBRequest::dispatchEvent(Event& event)
127{
128 ASSERT(&originThread() == &Thread::current());
129
130 auto protectedThis = makeRef(*this);
131
132 IDBRequest::dispatchEvent(event);
133
134 if (m_transaction && m_transaction->isVersionChange() && (event.type() == eventNames().errorEvent || event.type() == eventNames().successEvent))
135 m_transaction->database().connectionProxy().didFinishHandlingVersionChangeTransaction(m_transaction->database().databaseConnectionIdentifier(), *m_transaction);
136}
137
138void IDBOpenDBRequest::onSuccess(const IDBResultData& resultData)
139{
140 LOG(IndexedDB, "IDBOpenDBRequest::onSuccess()");
141
142 ASSERT(&originThread() == &Thread::current());
143
144 setResult(IDBDatabase::create(*scriptExecutionContext(), connectionProxy(), resultData));
145 m_readyState = ReadyState::Done;
146
147 enqueueEvent(IDBRequestCompletionEvent::create(eventNames().successEvent, Event::CanBubble::No, Event::IsCancelable::No, *this));
148}
149
150void IDBOpenDBRequest::onUpgradeNeeded(const IDBResultData& resultData)
151{
152 ASSERT(&originThread() == &Thread::current());
153
154 Ref<IDBDatabase> database = IDBDatabase::create(*scriptExecutionContext(), connectionProxy(), resultData);
155 Ref<IDBTransaction> transaction = database->startVersionChangeTransaction(resultData.transactionInfo(), *this);
156
157 ASSERT(transaction->info().mode() == IDBTransactionMode::Versionchange);
158 ASSERT(transaction->originalDatabaseInfo());
159
160 uint64_t oldVersion = transaction->originalDatabaseInfo()->version();
161 uint64_t newVersion = transaction->info().newVersion();
162
163 LOG(IndexedDB, "IDBOpenDBRequest::onUpgradeNeeded() - current version is %" PRIu64 ", new is %" PRIu64, oldVersion, newVersion);
164
165 setResult(WTFMove(database));
166 m_readyState = ReadyState::Done;
167 m_transaction = WTFMove(transaction);
168 m_transaction->addRequest(*this);
169
170 enqueueEvent(IDBVersionChangeEvent::create(oldVersion, newVersion, eventNames().upgradeneededEvent));
171}
172
173void IDBOpenDBRequest::onDeleteDatabaseSuccess(const IDBResultData& resultData)
174{
175 ASSERT(&originThread() == &Thread::current());
176
177 uint64_t oldVersion = resultData.databaseInfo().version();
178
179 LOG(IndexedDB, "IDBOpenDBRequest::onDeleteDatabaseSuccess() - current version is %" PRIu64, oldVersion);
180
181 m_readyState = ReadyState::Done;
182 setResultToUndefined();
183
184 enqueueEvent(IDBVersionChangeEvent::create(oldVersion, 0, eventNames().successEvent));
185}
186
187void IDBOpenDBRequest::requestCompleted(const IDBResultData& data)
188{
189 LOG(IndexedDB, "IDBOpenDBRequest::requestCompleted");
190
191 ASSERT(&originThread() == &Thread::current());
192
193 // If an Open request was completed after the page has navigated, leaving this request
194 // with a stopped script execution context, we need to message back to the server so it
195 // doesn't hang waiting on a database connection or transaction that will never exist.
196 if (m_contextStopped) {
197 switch (data.type()) {
198 case IDBResultType::OpenDatabaseSuccess:
199 connectionProxy().abortOpenAndUpgradeNeeded(data.databaseConnectionIdentifier(), IDBResourceIdentifier::emptyValue());
200 break;
201 case IDBResultType::OpenDatabaseUpgradeNeeded:
202 connectionProxy().abortOpenAndUpgradeNeeded(data.databaseConnectionIdentifier(), data.transactionInfo().identifier());
203 break;
204 default:
205 break;
206 }
207
208 return;
209 }
210
211 switch (data.type()) {
212 case IDBResultType::Error:
213 onError(data);
214 break;
215 case IDBResultType::OpenDatabaseSuccess:
216 onSuccess(data);
217 break;
218 case IDBResultType::OpenDatabaseUpgradeNeeded:
219 onUpgradeNeeded(data);
220 break;
221 case IDBResultType::DeleteDatabaseSuccess:
222 onDeleteDatabaseSuccess(data);
223 break;
224 default:
225 RELEASE_ASSERT_NOT_REACHED();
226 }
227}
228
229void IDBOpenDBRequest::requestBlocked(uint64_t oldVersion, uint64_t newVersion)
230{
231 ASSERT(&originThread() == &Thread::current());
232
233 LOG(IndexedDB, "IDBOpenDBRequest::requestBlocked");
234 enqueueEvent(IDBVersionChangeEvent::create(oldVersion, newVersion, eventNames().blockedEvent));
235}
236
237} // namespace WebCore
238
239#endif // ENABLE(INDEXED_DATABASE)
240