Modern IDB: storage/indexeddb/delete-in-upgradeneeded-close-in-versionchange.html...
[WebKit-https.git] / Source / WebCore / Modules / indexeddb / client / IDBRequestImpl.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 "IDBRequestImpl.h"
28
29 #if ENABLE(INDEXED_DATABASE)
30
31 #include "DOMRequestState.h"
32 #include "EventQueue.h"
33 #include "IDBBindingUtilities.h"
34 #include "IDBCursorImpl.h"
35 #include "IDBDatabaseException.h"
36 #include "IDBEventDispatcher.h"
37 #include "IDBKeyData.h"
38 #include "IDBResultData.h"
39 #include "Logging.h"
40 #include "ScriptExecutionContext.h"
41 #include "ThreadSafeDataBuffer.h"
42 #include <wtf/NeverDestroyed.h>
43
44 namespace WebCore {
45 namespace IDBClient {
46
47 Ref<IDBRequest> IDBRequest::create(ScriptExecutionContext& context, IDBObjectStore& objectStore, IDBTransaction& transaction)
48 {
49     return adoptRef(*new IDBRequest(context, objectStore, transaction));
50 }
51
52 Ref<IDBRequest> IDBRequest::create(ScriptExecutionContext& context, IDBCursor& cursor, IDBTransaction& transaction)
53 {
54     return adoptRef(*new IDBRequest(context, cursor, transaction));
55 }
56
57 Ref<IDBRequest> IDBRequest::createCount(ScriptExecutionContext& context, IDBIndex& index, IDBTransaction& transaction)
58 {
59     return adoptRef(*new IDBRequest(context, index, transaction));
60 }
61
62 Ref<IDBRequest> IDBRequest::createGet(ScriptExecutionContext& context, IDBIndex& index, IndexedDB::IndexRecordType requestedRecordType, IDBTransaction& transaction)
63 {
64     return adoptRef(*new IDBRequest(context, index, requestedRecordType, transaction));
65 }
66
67 IDBRequest::IDBRequest(IDBConnectionToServer& connection, ScriptExecutionContext* context)
68     : IDBOpenDBRequest(context)
69     , m_connection(connection)
70     , m_resourceIdentifier(connection)
71 {
72     suspendIfNeeded();
73 }
74
75 IDBRequest::IDBRequest(ScriptExecutionContext& context, IDBObjectStore& objectStore, IDBTransaction& transaction)
76     : IDBOpenDBRequest(&context)
77     , m_transaction(&transaction)
78     , m_connection(transaction.serverConnection())
79     , m_resourceIdentifier(transaction.serverConnection())
80     , m_source(IDBAny::create(objectStore))
81 {
82     suspendIfNeeded();
83 }
84
85 IDBRequest::IDBRequest(ScriptExecutionContext& context, IDBCursor& cursor, IDBTransaction& transaction)
86     : IDBOpenDBRequest(&context)
87     , m_transaction(&transaction)
88     , m_connection(transaction.serverConnection())
89     , m_resourceIdentifier(transaction.serverConnection())
90     , m_source(cursor.source())
91     , m_pendingCursor(&cursor)
92 {
93     suspendIfNeeded();
94
95     cursor.setRequest(*this);
96 }
97
98 IDBRequest::IDBRequest(ScriptExecutionContext& context, IDBIndex& index, IDBTransaction& transaction)
99     : IDBOpenDBRequest(&context)
100     , m_transaction(&transaction)
101     , m_connection(transaction.serverConnection())
102     , m_resourceIdentifier(transaction.serverConnection())
103     , m_source(IDBAny::create(index))
104 {
105     suspendIfNeeded();
106 }
107
108 IDBRequest::IDBRequest(ScriptExecutionContext& context, IDBIndex& index, IndexedDB::IndexRecordType requestedRecordType, IDBTransaction& transaction)
109     : IDBRequest(context, index, transaction)
110 {
111     m_requestedIndexRecordType = requestedRecordType;
112 }
113
114 IDBRequest::~IDBRequest()
115 {
116     if (m_result) {
117         auto type = m_result->type();
118         if (type == IDBAny::Type::IDBCursor || type == IDBAny::Type::IDBCursorWithValue)
119             m_result->modernIDBCursor()->clearRequest();
120     }
121 }
122
123 RefPtr<WebCore::IDBAny> IDBRequest::result(ExceptionCodeWithMessage& ec) const
124 {
125     if (m_readyState == IDBRequestReadyState::Done)
126         return m_result;
127
128     ec.code = IDBDatabaseException::InvalidStateError;
129     ec.message = ASCIILiteral("Failed to read the 'result' property from 'IDBRequest': The request has not finished.");
130     return nullptr;
131 }
132
133 unsigned short IDBRequest::errorCode(ExceptionCode&) const
134 {
135     return 0;
136 }
137
138 RefPtr<DOMError> IDBRequest::error(ExceptionCodeWithMessage& ec) const
139 {
140     if (m_readyState == IDBRequestReadyState::Done)
141         return m_domError;
142
143     ec.code = IDBDatabaseException::InvalidStateError;
144     ec.message = ASCIILiteral("Failed to read the 'error' property from 'IDBRequest': The request has not finished.");
145     return nullptr;
146 }
147
148 RefPtr<WebCore::IDBAny> IDBRequest::source() const
149 {
150     return m_source;
151 }
152
153 void IDBRequest::setSource(IDBCursor& cursor)
154 {
155     m_source = IDBAny::create(cursor);
156 }
157
158 RefPtr<WebCore::IDBTransaction> IDBRequest::transaction() const
159 {
160     return m_shouldExposeTransactionToDOM ? m_transaction : nullptr;
161 }
162
163 const String& IDBRequest::readyState() const
164 {
165     static WTF::NeverDestroyed<String> pendingString("pending");
166     static WTF::NeverDestroyed<String> doneString("done");
167
168     switch (m_readyState) {
169     case IDBRequestReadyState::Pending:
170         return pendingString;
171     case IDBRequestReadyState::Done:
172         return doneString;
173     default:
174         RELEASE_ASSERT_NOT_REACHED();
175     }
176 }
177
178 uint64_t IDBRequest::sourceObjectStoreIdentifier() const
179 {
180     if (!m_source)
181         return 0;
182
183     if (m_source->type() == IDBAny::Type::IDBObjectStore) {
184         auto* objectStore = m_source->modernIDBObjectStore();
185         if (!objectStore)
186             return 0;
187         return objectStore->info().identifier();
188     }
189
190     if (m_source->type() == IDBAny::Type::IDBIndex) {
191         auto* index = m_source->modernIDBIndex();
192         if (!index)
193             return 0;
194         return index->info().objectStoreIdentifier();
195     }
196
197     return 0;
198 }
199
200 uint64_t IDBRequest::sourceIndexIdentifier() const
201 {
202     if (!m_source)
203         return 0;
204     if (m_source->type() != IDBAny::Type::IDBIndex)
205         return 0;
206     if (!m_source->modernIDBIndex())
207         return 0;
208
209     return m_source->modernIDBIndex()->info().identifier();
210 }
211
212 IndexedDB::IndexRecordType IDBRequest::requestedIndexRecordType() const
213 {
214     ASSERT(m_source);
215     ASSERT(m_source->type() == IDBAny::Type::IDBIndex);
216
217     return m_requestedIndexRecordType;
218 }
219
220 EventTargetInterface IDBRequest::eventTargetInterface() const
221 {
222     return IDBRequestEventTargetInterfaceType;
223 }
224
225 const char* IDBRequest::activeDOMObjectName() const
226 {
227     return "IDBRequest";
228 }
229
230 bool IDBRequest::canSuspendForDocumentSuspension() const
231 {
232     return false;
233 }
234
235 bool IDBRequest::hasPendingActivity() const
236 {
237     return m_hasPendingActivity;
238 }
239
240 void IDBRequest::stop()
241 {
242     ASSERT(!m_contextStopped);
243     m_contextStopped = true;
244 }
245
246 void IDBRequest::enqueueEvent(Ref<Event>&& event)
247 {
248     if (!scriptExecutionContext() || m_contextStopped)
249         return;
250
251     event->setTarget(this);
252     scriptExecutionContext()->eventQueue().enqueueEvent(WTF::move(event));
253 }
254
255 bool IDBRequest::dispatchEvent(Event& event)
256 {
257     LOG(IndexedDB, "IDBRequest::dispatchEvent - %s (%p)", event.type().characters8(), this);
258
259     ASSERT(m_hasPendingActivity);
260     ASSERT(!m_contextStopped);
261
262     if (event.type() != eventNames().blockedEvent)
263         m_readyState = IDBRequestReadyState::Done;
264
265     Vector<RefPtr<EventTarget>> targets;
266     targets.append(this);
267
268     if (m_transaction) {
269         if (!m_transaction->isFinished())
270             targets.append(m_transaction);
271         if (!m_transaction->database().isClosingOrClosed())
272             targets.append(m_transaction->db());
273     }
274
275     m_hasPendingActivity = false;
276
277     bool dontPreventDefault;
278     {
279         TransactionActivator activator(m_transaction.get());
280         dontPreventDefault = IDBEventDispatcher::dispatch(event, targets);
281     }
282
283     // IDBEventDispatcher::dispatch() might have set the pending activity flag back to true, suggesting the request will be reused.
284     // We might also re-use the request if this event was the upgradeneeded event for an IDBOpenDBRequest.
285     if (!m_hasPendingActivity)
286         m_hasPendingActivity = isOpenDBRequest() && (event.type() == eventNames().upgradeneededEvent || event.type() == eventNames().blockedEvent);
287
288     // The request should only remain in the transaction's request list if it represents a pending cursor operation, or this is an open request that was blocked.
289     if (m_transaction && !m_pendingCursor && event.type() != eventNames().blockedEvent)
290         m_transaction->removeRequest(*this);
291
292     if (dontPreventDefault && event.type() == eventNames().errorEvent && m_transaction && !m_transaction->isFinishedOrFinishing()) {
293         ASSERT(m_domError);
294         m_transaction->abortDueToFailedRequest(*m_domError);
295     }
296
297     return dontPreventDefault;
298 }
299
300 void IDBRequest::uncaughtExceptionInEventHandler()
301 {
302     LOG(IndexedDB, "IDBRequest::uncaughtExceptionInEventHandler");
303
304     if (m_transaction && m_idbError.code() != IDBDatabaseException::AbortError)
305         m_transaction->abortDueToFailedRequest(DOMError::create(IDBDatabaseException::getErrorName(IDBDatabaseException::AbortError)));
306 }
307
308 void IDBRequest::setResult(const IDBKeyData* keyData)
309 {
310     if (!keyData) {
311         m_result = nullptr;
312         return;
313     }
314
315     Deprecated::ScriptValue value = idbKeyDataToScriptValue(scriptExecutionContext(), *keyData);
316     m_result = IDBAny::create(WTF::move(value));
317 }
318
319 void IDBRequest::setResult(uint64_t number)
320 {
321     ASSERT(scriptExecutionContext());
322     m_result = IDBAny::create(Deprecated::ScriptValue(scriptExecutionContext()->vm(), JSC::JSValue(number)));
323 }
324
325 void IDBRequest::setResultToStructuredClone(const ThreadSafeDataBuffer& valueData)
326 {
327     LOG(IndexedDB, "IDBRequest::setResultToStructuredClone");
328
329     auto context = scriptExecutionContext();
330     if (!context)
331         return;
332
333     Deprecated::ScriptValue value = deserializeIDBValueData(*context, valueData);
334     m_result = IDBAny::create(WTF::move(value));
335 }
336
337 void IDBRequest::setResultToUndefined()
338 {
339     m_result = IDBAny::createUndefined();
340 }
341
342 IDBCursor* IDBRequest::resultCursor()
343 {
344     if (!m_result)
345         return nullptr;
346     if (m_result->type() == IDBAny::Type::IDBCursor || m_result->type() == IDBAny::Type::IDBCursorWithValue)
347         return m_result->modernIDBCursor();
348     return nullptr;
349 }
350
351 void IDBRequest::willIterateCursor(IDBCursor& cursor)
352 {
353     ASSERT(m_readyState == IDBRequestReadyState::Done);
354     ASSERT(scriptExecutionContext());
355     ASSERT(m_transaction);
356     ASSERT(!m_pendingCursor);
357     ASSERT(&cursor == resultCursor());
358
359     m_pendingCursor = &cursor;
360     m_hasPendingActivity = true;
361     m_result = nullptr;
362     m_readyState = IDBRequestReadyState::Pending;
363     m_domError = nullptr;
364     m_idbError = { };
365 }
366
367 void IDBRequest::didOpenOrIterateCursor(const IDBResultData& resultData)
368 {
369     ASSERT(m_pendingCursor);
370     m_result = nullptr;
371
372     if (resultData.type() == IDBResultType::IterateCursorSuccess || resultData.type() == IDBResultType::OpenCursorSuccess) {
373         m_pendingCursor->setGetResult(*this, resultData.getResult());
374         if (resultData.getResult().isDefined())
375             m_result = IDBAny::create(*m_pendingCursor);
376     }
377
378     m_pendingCursor = nullptr;
379
380     requestCompleted(resultData);
381 }
382
383 void IDBRequest::requestCompleted(const IDBResultData& resultData)
384 {
385     m_readyState = IDBRequestReadyState::Done;
386
387     m_idbError = resultData.error();
388     if (!m_idbError.isNull())
389         onError();
390     else
391         onSuccess();
392 }
393
394 void IDBRequest::onError()
395 {
396     LOG(IndexedDB, "IDBRequest::onError");
397
398     ASSERT(!m_idbError.isNull());
399     m_domError = DOMError::create(m_idbError.name());
400     enqueueEvent(Event::create(eventNames().errorEvent, true, true));
401 }
402
403 void IDBRequest::onSuccess()
404 {
405     LOG(IndexedDB, "IDBRequest::onSuccess");
406
407     enqueueEvent(Event::create(eventNames().successEvent, false, false));
408 }
409
410 } // namespace IDBClient
411 } // namespace WebCore
412
413 #endif // ENABLE(INDEXED_DATABASE)