1 /*
2  * Copyright (C) 2010 Google 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  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include "third_party/blink/renderer/modules/indexeddb/idb_request.h"
30 
31 #include <atomic>
32 #include <memory>
33 #include <utility>
34 
35 #include "third_party/blink/public/platform/web_blob_info.h"
36 #include "third_party/blink/renderer/bindings/core/v8/to_v8_for_core.h"
37 #include "third_party/blink/renderer/bindings/modules/v8/idb_object_store_or_idb_index_or_idb_cursor.h"
38 #include "third_party/blink/renderer/bindings/modules/v8/to_v8_for_modules.h"
39 #include "third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules.h"
40 #include "third_party/blink/renderer/core/dom/dom_exception.h"
41 #include "third_party/blink/renderer/core/dom/events/event_queue.h"
42 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
43 #include "third_party/blink/renderer/modules/indexed_db_names.h"
44 #include "third_party/blink/renderer/modules/indexeddb/idb_cursor_with_value.h"
45 #include "third_party/blink/renderer/modules/indexeddb/idb_database.h"
46 #include "third_party/blink/renderer/modules/indexeddb/idb_event_dispatcher.h"
47 #include "third_party/blink/renderer/modules/indexeddb/idb_request_queue_item.h"
48 #include "third_party/blink/renderer/modules/indexeddb/idb_tracing.h"
49 #include "third_party/blink/renderer/modules/indexeddb/idb_value.h"
50 #include "third_party/blink/renderer/modules/indexeddb/idb_value_wrapping.h"
51 #include "third_party/blink/renderer/modules/indexeddb/web_idb_callbacks_impl.h"
52 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
53 #include "third_party/blink/renderer/platform/heap/handle.h"
54 #include "third_party/blink/renderer/platform/heap/heap.h"
55 #include "third_party/blink/renderer/platform/wtf/functional.h"
56 #include "third_party/blink/renderer/platform/wtf/shared_buffer.h"
57 
58 namespace blink {
59 
AsyncTraceState(const char * trace_event_name)60 IDBRequest::AsyncTraceState::AsyncTraceState(const char* trace_event_name)
61     : trace_event_name_(nullptr) {
62   // If PopulateForNewEvent is called, it sets trace_event_name_ to
63   // trace_event_name. Otherwise, trace_event_name_ is nullptr, so this instance
64   // is considered empty. This roundabout initialization lets us avoid calling
65   // TRACE_EVENT_NESTABLE_ASYNC_END0 with an uninitalized ID.
66   TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(
67       "IndexedDB", trace_event_name,
68       TRACE_ID_LOCAL(PopulateForNewEvent(trace_event_name)));
69 }
70 
RecordAndReset()71 void IDBRequest::AsyncTraceState::RecordAndReset() {
72   if (trace_event_name_) {
73     TRACE_EVENT_NESTABLE_ASYNC_END0("IndexedDB", trace_event_name_,
74                                     TRACE_ID_LOCAL(id_));
75     trace_event_name_ = nullptr;
76   }
77 }
78 
~AsyncTraceState()79 IDBRequest::AsyncTraceState::~AsyncTraceState() {
80   if (trace_event_name_) {
81     TRACE_EVENT_NESTABLE_ASYNC_END0("IndexedDB", trace_event_name_,
82                                     TRACE_ID_LOCAL(id_));
83   }
84 }
85 
PopulateForNewEvent(const char * trace_event_name)86 size_t IDBRequest::AsyncTraceState::PopulateForNewEvent(
87     const char* trace_event_name) {
88   DCHECK(trace_event_name);
89   DCHECK(!trace_event_name_);
90   trace_event_name_ = trace_event_name;
91 
92   static std::atomic<size_t> counter(0);
93   id_ = counter.fetch_add(1, std::memory_order_relaxed);
94   return id_;
95 }
96 
Create(ScriptState * script_state,IDBIndex * source,IDBTransaction * transaction,IDBRequest::AsyncTraceState metrics)97 IDBRequest* IDBRequest::Create(ScriptState* script_state,
98                                IDBIndex* source,
99                                IDBTransaction* transaction,
100                                IDBRequest::AsyncTraceState metrics) {
101   return IDBRequest::Create(script_state, Source::FromIDBIndex(source),
102                             transaction, std::move(metrics));
103 }
104 
Create(ScriptState * script_state,IDBObjectStore * source,IDBTransaction * transaction,IDBRequest::AsyncTraceState metrics)105 IDBRequest* IDBRequest::Create(ScriptState* script_state,
106                                IDBObjectStore* source,
107                                IDBTransaction* transaction,
108                                IDBRequest::AsyncTraceState metrics) {
109   return IDBRequest::Create(script_state, Source::FromIDBObjectStore(source),
110                             transaction, std::move(metrics));
111 }
112 
Create(ScriptState * script_state,IDBCursor * source,IDBTransaction * transaction,IDBRequest::AsyncTraceState metrics)113 IDBRequest* IDBRequest::Create(ScriptState* script_state,
114                                IDBCursor* source,
115                                IDBTransaction* transaction,
116                                IDBRequest::AsyncTraceState metrics) {
117   return IDBRequest::Create(script_state, Source::FromIDBCursor(source),
118                             transaction, std::move(metrics));
119 }
120 
Create(ScriptState * script_state,const Source & source,IDBTransaction * transaction,IDBRequest::AsyncTraceState metrics)121 IDBRequest* IDBRequest::Create(ScriptState* script_state,
122                                const Source& source,
123                                IDBTransaction* transaction,
124                                IDBRequest::AsyncTraceState metrics) {
125   IDBRequest* request = MakeGarbageCollected<IDBRequest>(
126       script_state, source, transaction, std::move(metrics));
127   // Requests associated with IDBFactory (open/deleteDatabase/getDatabaseNames)
128   // do not have an associated transaction.
129   if (transaction)
130     transaction->RegisterRequest(request);
131   return request;
132 }
133 
IDBRequest(ScriptState * script_state,const Source & source,IDBTransaction * transaction,AsyncTraceState metrics)134 IDBRequest::IDBRequest(ScriptState* script_state,
135                        const Source& source,
136                        IDBTransaction* transaction,
137                        AsyncTraceState metrics)
138     : ExecutionContextLifecycleObserver(ExecutionContext::From(script_state)),
139       transaction_(transaction),
140       isolate_(script_state->GetIsolate()),
141       metrics_(std::move(metrics)),
142       source_(source),
143       event_queue_(
144           MakeGarbageCollected<EventQueue>(ExecutionContext::From(script_state),
145                                            TaskType::kDatabaseAccess)) {}
146 
~IDBRequest()147 IDBRequest::~IDBRequest() {
148   if (!GetExecutionContext())
149     return;
150   if (ready_state_ == DONE)
151     DCHECK(metrics_.IsEmpty()) << metrics_.trace_event_name();
152   else
153     DCHECK_EQ(ready_state_, kEarlyDeath);
154 }
155 
Trace(Visitor * visitor) const156 void IDBRequest::Trace(Visitor* visitor) const {
157   visitor->Trace(transaction_);
158   visitor->Trace(source_);
159   visitor->Trace(result_);
160   visitor->Trace(error_);
161   visitor->Trace(event_queue_);
162   visitor->Trace(pending_cursor_);
163   EventTargetWithInlineData::Trace(visitor);
164   ExecutionContextLifecycleObserver::Trace(visitor);
165 }
166 
result(ScriptState * script_state,ExceptionState & exception_state)167 ScriptValue IDBRequest::result(ScriptState* script_state,
168                                ExceptionState& exception_state) {
169   if (ready_state_ != DONE) {
170     // Must throw if returning an empty value. Message is arbitrary since it
171     // will never be seen.
172     exception_state.ThrowDOMException(
173         DOMExceptionCode::kInvalidStateError,
174         IDBDatabase::kRequestNotFinishedErrorMessage);
175     return ScriptValue();
176   }
177   if (!GetExecutionContext()) {
178     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
179                                       IDBDatabase::kDatabaseClosedErrorMessage);
180     return ScriptValue();
181   }
182   result_dirty_ = false;
183   ScriptValue value = ScriptValue::From(script_state, result_);
184   return value;
185 }
186 
error(ExceptionState & exception_state) const187 DOMException* IDBRequest::error(ExceptionState& exception_state) const {
188   if (ready_state_ != DONE) {
189     exception_state.ThrowDOMException(
190         DOMExceptionCode::kInvalidStateError,
191         IDBDatabase::kRequestNotFinishedErrorMessage);
192     return nullptr;
193   }
194   return error_;
195 }
196 
source(ScriptState * script_state,IDBObjectStoreOrIDBIndexOrIDBCursor & source) const197 void IDBRequest::source(ScriptState* script_state,
198                         IDBObjectStoreOrIDBIndexOrIDBCursor& source) const {
199   if (!GetExecutionContext()) {
200     source = Source();
201   }
202   source = source_;
203 }
204 
readyState() const205 const String& IDBRequest::readyState() const {
206   DCHECK(ready_state_ == PENDING || ready_state_ == DONE);
207 
208   if (ready_state_ == PENDING)
209     return indexed_db_names::kPending;
210 
211   return indexed_db_names::kDone;
212 }
213 
CreateWebCallbacks()214 std::unique_ptr<WebIDBCallbacks> IDBRequest::CreateWebCallbacks() {
215   DCHECK(!web_callbacks_);
216   auto callbacks = std::make_unique<WebIDBCallbacksImpl>(this);
217   web_callbacks_ = callbacks.get();
218   return callbacks;
219 }
220 
Abort()221 void IDBRequest::Abort() {
222   DCHECK(!request_aborted_);
223   if (queue_item_) {
224     queue_item_->CancelLoading();
225 
226     // A transaction's requests are aborted in order, so each aborted request
227     // should immediately get out of the result queue.
228     DCHECK(!queue_item_);
229   }
230 
231   if (!GetExecutionContext())
232     return;
233   DCHECK(ready_state_ == PENDING || ready_state_ == DONE);
234   if (ready_state_ == DONE)
235     return;
236 
237   event_queue_->CancelAllEvents();
238 
239   error_.Clear();
240   result_.Clear();
241   EnqueueResponse(MakeGarbageCollected<DOMException>(
242       DOMExceptionCode::kAbortError,
243       "The transaction was aborted, so the request cannot be fulfilled."));
244   request_aborted_ = true;
245 }
246 
SetCursorDetails(indexed_db::CursorType cursor_type,mojom::IDBCursorDirection direction)247 void IDBRequest::SetCursorDetails(indexed_db::CursorType cursor_type,
248                                   mojom::IDBCursorDirection direction) {
249   DCHECK_EQ(ready_state_, PENDING);
250   DCHECK(!pending_cursor_);
251   cursor_type_ = cursor_type;
252   cursor_direction_ = direction;
253 }
254 
SetPendingCursor(IDBCursor * cursor)255 void IDBRequest::SetPendingCursor(IDBCursor* cursor) {
256   DCHECK_EQ(ready_state_, DONE);
257   DCHECK(GetExecutionContext());
258   DCHECK(transaction_);
259   DCHECK(!pending_cursor_);
260   DCHECK_EQ(cursor, GetResultCursor());
261 
262   has_pending_activity_ = true;
263   pending_cursor_ = cursor;
264   SetResult(nullptr);
265   ready_state_ = PENDING;
266   error_.Clear();
267   transaction_->RegisterRequest(this);
268 }
269 
GetResultCursor() const270 IDBCursor* IDBRequest::GetResultCursor() const {
271   if (!result_)
272     return nullptr;
273   if (result_->GetType() == IDBAny::kIDBCursorType)
274     return result_->IdbCursor();
275   if (result_->GetType() == IDBAny::kIDBCursorWithValueType)
276     return result_->IdbCursorWithValue();
277   return nullptr;
278 }
279 
SetResultCursor(IDBCursor * cursor,std::unique_ptr<IDBKey> key,std::unique_ptr<IDBKey> primary_key,std::unique_ptr<IDBValue> value)280 void IDBRequest::SetResultCursor(IDBCursor* cursor,
281                                  std::unique_ptr<IDBKey> key,
282                                  std::unique_ptr<IDBKey> primary_key,
283                                  std::unique_ptr<IDBValue> value) {
284   DCHECK_EQ(ready_state_, PENDING);
285   cursor_key_ = std::move(key);
286   cursor_primary_key_ = std::move(primary_key);
287   cursor_value_ = std::move(value);
288 
289   EnqueueResultInternal(MakeGarbageCollected<IDBAny>(cursor));
290 }
291 
ShouldEnqueueEvent() const292 bool IDBRequest::ShouldEnqueueEvent() const {
293   const ExecutionContext* execution_context = GetExecutionContext();
294 
295   // https://crbug.com/733642 - Document::Shutdown() calls
296   // LocalDOMWindow::ClearEventQueue(), which nulls out the context's event
297   // queue, before calling ExecutionContext::NotifyContextDestroyed(). The
298   // latter eventually calls IDBRequest::ContextDestroyed(), which aborts the
299   // request. As an aborted IDBRequest is removed from its' IDBTransaction
300   // result queue, it may unblock another request whose result is already
301   // available. If the unblocked request hasn't received a
302   // NotifyContextDestroyed() call yet, it will hang onto an ExecutionContext
303   // whose event queue has been nulled out. The event queue null check covers
304   // these specific circumstances.
305   if (!execution_context)
306     return false;
307 
308   DCHECK(ready_state_ == PENDING || ready_state_ == DONE);
309   if (request_aborted_)
310     return false;
311   DCHECK_EQ(ready_state_, PENDING);
312   DCHECK(!error_ && !result_);
313   return true;
314 }
315 
HandleResponse(DOMException * error)316 void IDBRequest::HandleResponse(DOMException* error) {
317   transit_blob_handles_.clear();
318   if (!transaction_ || !transaction_->HasQueuedResults())
319     return EnqueueResponse(error);
320   transaction_->EnqueueResult(std::make_unique<IDBRequestQueueItem>(
321       this, error,
322       WTF::Bind(&IDBTransaction::OnResultReady,
323                 WrapPersistent(transaction_.Get()))));
324 }
325 
HandleResponse(std::unique_ptr<IDBKey> key)326 void IDBRequest::HandleResponse(std::unique_ptr<IDBKey> key) {
327   transit_blob_handles_.clear();
328   DCHECK(transaction_);
329   if (!transaction_->HasQueuedResults())
330     return EnqueueResponse(std::move(key));
331   transaction_->EnqueueResult(std::make_unique<IDBRequestQueueItem>(
332       this, std::move(key),
333       WTF::Bind(&IDBTransaction::OnResultReady,
334                 WrapPersistent(transaction_.Get()))));
335 }
336 
HandleResponse(int64_t value_or_old_version)337 void IDBRequest::HandleResponse(int64_t value_or_old_version) {
338   DCHECK(transit_blob_handles_.IsEmpty());
339   if (!transaction_ || !transaction_->HasQueuedResults())
340     return EnqueueResponse(value_or_old_version);
341   transaction_->EnqueueResult(std::make_unique<IDBRequestQueueItem>(
342       this, value_or_old_version,
343       WTF::Bind(&IDBTransaction::OnResultReady,
344                 WrapPersistent(transaction_.Get()))));
345 }
346 
HandleResponse()347 void IDBRequest::HandleResponse() {
348   transit_blob_handles_.clear();
349   if (!transaction_ || !transaction_->HasQueuedResults())
350     return EnqueueResponse();
351   transaction_->EnqueueResult(std::make_unique<IDBRequestQueueItem>(
352       this, WTF::Bind(&IDBTransaction::OnResultReady,
353                       WrapPersistent(transaction_.Get()))));
354 }
355 
HandleResponse(std::unique_ptr<WebIDBCursor> backend,std::unique_ptr<IDBKey> key,std::unique_ptr<IDBKey> primary_key,std::unique_ptr<IDBValue> value)356 void IDBRequest::HandleResponse(std::unique_ptr<WebIDBCursor> backend,
357                                 std::unique_ptr<IDBKey> key,
358                                 std::unique_ptr<IDBKey> primary_key,
359                                 std::unique_ptr<IDBValue> value) {
360   DCHECK(transit_blob_handles_.IsEmpty());
361   DCHECK(transaction_);
362   bool is_wrapped = IDBValueUnwrapper::IsWrapped(value.get());
363   if (!transaction_->HasQueuedResults() && !is_wrapped) {
364     return EnqueueResponse(std::move(backend), std::move(key),
365                            std::move(primary_key), std::move(value));
366   }
367   transaction_->EnqueueResult(std::make_unique<IDBRequestQueueItem>(
368       this, std::move(backend), std::move(key), std::move(primary_key),
369       std::move(value), is_wrapped,
370       WTF::Bind(&IDBTransaction::OnResultReady,
371                 WrapPersistent(transaction_.Get()))));
372 }
373 
HandleResponse(std::unique_ptr<IDBValue> value)374 void IDBRequest::HandleResponse(std::unique_ptr<IDBValue> value) {
375   DCHECK(transit_blob_handles_.IsEmpty());
376   DCHECK(transaction_);
377   bool is_wrapped = IDBValueUnwrapper::IsWrapped(value.get());
378   if (!transaction_->HasQueuedResults() && !is_wrapped)
379     return EnqueueResponse(std::move(value));
380   transaction_->EnqueueResult(std::make_unique<IDBRequestQueueItem>(
381       this, std::move(value), is_wrapped,
382       WTF::Bind(&IDBTransaction::OnResultReady,
383                 WrapPersistent(transaction_.Get()))));
384 }
385 
HandleResponse(Vector<std::unique_ptr<IDBValue>> values)386 void IDBRequest::HandleResponse(Vector<std::unique_ptr<IDBValue>> values) {
387   DCHECK(transit_blob_handles_.IsEmpty());
388   DCHECK(transaction_);
389   bool is_wrapped = IDBValueUnwrapper::IsWrapped(values);
390   if (!transaction_->HasQueuedResults() && !is_wrapped)
391     return EnqueueResponse(std::move(values));
392   transaction_->EnqueueResult(std::make_unique<IDBRequestQueueItem>(
393       this, std::move(values), is_wrapped,
394       WTF::Bind(&IDBTransaction::OnResultReady,
395                 WrapPersistent(transaction_.Get()))));
396 }
397 
HandleResponse(std::unique_ptr<IDBKey> key,std::unique_ptr<IDBKey> primary_key,std::unique_ptr<IDBValue> value)398 void IDBRequest::HandleResponse(std::unique_ptr<IDBKey> key,
399                                 std::unique_ptr<IDBKey> primary_key,
400                                 std::unique_ptr<IDBValue> value) {
401   DCHECK(transit_blob_handles_.IsEmpty());
402   DCHECK(transaction_);
403   bool is_wrapped = IDBValueUnwrapper::IsWrapped(value.get());
404   if (!transaction_->HasQueuedResults() && !is_wrapped) {
405     return EnqueueResponse(std::move(key), std::move(primary_key),
406                            std::move(value));
407   }
408 
409   transaction_->EnqueueResult(std::make_unique<IDBRequestQueueItem>(
410       this, std::move(key), std::move(primary_key), std::move(value),
411       is_wrapped,
412       WTF::Bind(&IDBTransaction::OnResultReady,
413                 WrapPersistent(transaction_.Get()))));
414 }
415 
HandleResponse(bool key_only,mojo::PendingReceiver<mojom::blink::IDBDatabaseGetAllResultSink> receiver)416 void IDBRequest::HandleResponse(
417     bool key_only,
418     mojo::PendingReceiver<mojom::blink::IDBDatabaseGetAllResultSink> receiver) {
419   DCHECK(transit_blob_handles_.IsEmpty());
420   DCHECK(transaction_);
421   transaction_->EnqueueResult(std::make_unique<IDBRequestQueueItem>(
422       this, key_only, std::move(receiver),
423       WTF::Bind(&IDBTransaction::OnResultReady,
424                 WrapPersistent(transaction_.Get()))));
425 }
426 
EnqueueResponse(DOMException * error)427 void IDBRequest::EnqueueResponse(DOMException* error) {
428   IDB_TRACE("IDBRequest::EnqueueResponse(DOMException)");
429   if (!ShouldEnqueueEvent()) {
430     metrics_.RecordAndReset();
431     return;
432   }
433 
434   error_ = error;
435   SetResult(MakeGarbageCollected<IDBAny>(IDBAny::kUndefinedType));
436   pending_cursor_.Clear();
437   EnqueueEvent(Event::CreateCancelableBubble(event_type_names::kError));
438 }
439 
EnqueueResponse(const Vector<String> & string_list)440 void IDBRequest::EnqueueResponse(const Vector<String>& string_list) {
441   IDB_TRACE("IDBRequest::onSuccess(StringList)");
442   if (!ShouldEnqueueEvent()) {
443     metrics_.RecordAndReset();
444     return;
445   }
446 
447   auto* dom_string_list = MakeGarbageCollected<DOMStringList>();
448   for (const auto& item : string_list)
449     dom_string_list->Append(item);
450   EnqueueResultInternal(MakeGarbageCollected<IDBAny>(dom_string_list));
451 }
452 
EnqueueResponse(std::unique_ptr<WebIDBCursor> backend,std::unique_ptr<IDBKey> key,std::unique_ptr<IDBKey> primary_key,std::unique_ptr<IDBValue> value)453 void IDBRequest::EnqueueResponse(std::unique_ptr<WebIDBCursor> backend,
454                                  std::unique_ptr<IDBKey> key,
455                                  std::unique_ptr<IDBKey> primary_key,
456                                  std::unique_ptr<IDBValue> value) {
457   IDB_TRACE1("IDBRequest::EnqueueResponse(IDBCursor)", "size",
458              value ? value->DataSize() : 0);
459   if (!ShouldEnqueueEvent()) {
460     metrics_.RecordAndReset();
461     return;
462   }
463 
464   DCHECK(!pending_cursor_);
465   IDBCursor* cursor = nullptr;
466   IDBObjectStoreOrIDBIndex source;
467 
468   if (source_.IsIDBObjectStore()) {
469     source =
470         IDBCursor::Source::FromIDBObjectStore(source_.GetAsIDBObjectStore());
471   } else if (source_.IsIDBIndex()) {
472     source = IDBCursor::Source::FromIDBIndex(source_.GetAsIDBIndex());
473   }
474   DCHECK(!source.IsNull());
475 
476   switch (cursor_type_) {
477     case indexed_db::kCursorKeyOnly:
478       cursor =
479           MakeGarbageCollected<IDBCursor>(std::move(backend), cursor_direction_,
480                                           this, source, transaction_.Get());
481       break;
482     case indexed_db::kCursorKeyAndValue:
483       cursor = MakeGarbageCollected<IDBCursorWithValue>(
484           std::move(backend), cursor_direction_, this, source,
485           transaction_.Get());
486       break;
487     default:
488       NOTREACHED();
489   }
490   SetResultCursor(cursor, std::move(key), std::move(primary_key),
491                   std::move(value));
492 }
493 
EnqueueResponse(std::unique_ptr<IDBKey> idb_key)494 void IDBRequest::EnqueueResponse(std::unique_ptr<IDBKey> idb_key) {
495   IDB_TRACE("IDBRequest::EnqueueResponse(IDBKey)");
496   if (!ShouldEnqueueEvent()) {
497     metrics_.RecordAndReset();
498     return;
499   }
500 
501   if (idb_key && idb_key->IsValid())
502     EnqueueResultInternal(MakeGarbageCollected<IDBAny>(std::move(idb_key)));
503   else
504     EnqueueResultInternal(MakeGarbageCollected<IDBAny>(IDBAny::kUndefinedType));
505 }
506 
507 namespace {
SizeOfValues(const Vector<std::unique_ptr<IDBValue>> & values)508 size_t SizeOfValues(const Vector<std::unique_ptr<IDBValue>>& values) {
509   size_t size = 0;
510   for (const auto& value : values)
511     size += value->DataSize();
512   return size;
513 }
514 }  // namespace
515 
EnqueueResponse(Vector<std::unique_ptr<IDBValue>> values)516 void IDBRequest::EnqueueResponse(Vector<std::unique_ptr<IDBValue>> values) {
517   IDB_TRACE1("IDBRequest::EnqueueResponse([IDBValue])", "size",
518              SizeOfValues(values));
519   if (!ShouldEnqueueEvent()) {
520     metrics_.RecordAndReset();
521     return;
522   }
523 
524   EnqueueResultInternal(MakeGarbageCollected<IDBAny>(std::move(values)));
525 }
526 
527 #if DCHECK_IS_ON()
EffectiveObjectStore(const IDBRequest::Source & source)528 static IDBObjectStore* EffectiveObjectStore(const IDBRequest::Source& source) {
529   if (source.IsIDBObjectStore())
530     return source.GetAsIDBObjectStore();
531   if (source.IsIDBIndex())
532     return source.GetAsIDBIndex()->objectStore();
533 
534   NOTREACHED();
535   return nullptr;
536 }
537 #endif  // DCHECK_IS_ON()
538 
EnqueueResponse(std::unique_ptr<IDBValue> value)539 void IDBRequest::EnqueueResponse(std::unique_ptr<IDBValue> value) {
540   IDB_TRACE1("IDBRequest::EnqueueResponse(IDBValue)", "size",
541              value ? value->DataSize() : 0);
542   if (!ShouldEnqueueEvent()) {
543     metrics_.RecordAndReset();
544     return;
545   }
546 
547   if (pending_cursor_) {
548     // Value should be null, signifying the end of the cursor's range.
549     DCHECK(value->IsNull());
550     DCHECK(!value->BlobInfo().size());
551     pending_cursor_->Close();
552     pending_cursor_.Clear();
553   }
554 
555 #if DCHECK_IS_ON()
556   DCHECK(!value->PrimaryKey() ||
557          value->KeyPath() == EffectiveObjectStore(source_)->IdbKeyPath());
558 #endif
559 
560   EnqueueResultInternal(MakeGarbageCollected<IDBAny>(std::move(value)));
561 }
562 
EnqueueResponse(int64_t value)563 void IDBRequest::EnqueueResponse(int64_t value) {
564   IDB_TRACE("IDBRequest::EnqueueResponse(int64_t)");
565   if (!ShouldEnqueueEvent()) {
566     metrics_.RecordAndReset();
567     return;
568   }
569   EnqueueResultInternal(MakeGarbageCollected<IDBAny>(value));
570 }
571 
EnqueueResponse()572 void IDBRequest::EnqueueResponse() {
573   IDB_TRACE("IDBRequest::EnqueueResponse()");
574   if (!ShouldEnqueueEvent()) {
575     metrics_.RecordAndReset();
576     return;
577   }
578   EnqueueResultInternal(MakeGarbageCollected<IDBAny>(IDBAny::kUndefinedType));
579 }
580 
EnqueueResultInternal(IDBAny * result)581 void IDBRequest::EnqueueResultInternal(IDBAny* result) {
582   DCHECK(GetExecutionContext());
583   DCHECK(!pending_cursor_);
584   DCHECK(transit_blob_handles_.IsEmpty());
585   SetResult(result);
586   EnqueueEvent(Event::Create(event_type_names::kSuccess));
587 }
588 
SetResult(IDBAny * result)589 void IDBRequest::SetResult(IDBAny* result) {
590   result_ = result;
591   result_dirty_ = true;
592 }
593 
EnqueueResponse(std::unique_ptr<IDBKey> key,std::unique_ptr<IDBKey> primary_key,std::unique_ptr<IDBValue> value)594 void IDBRequest::EnqueueResponse(std::unique_ptr<IDBKey> key,
595                                  std::unique_ptr<IDBKey> primary_key,
596                                  std::unique_ptr<IDBValue> value) {
597   IDB_TRACE("IDBRequest::EnqueueResponse(IDBKey, IDBKey primaryKey, IDBValue)");
598   if (!ShouldEnqueueEvent()) {
599     metrics_.RecordAndReset();
600     return;
601   }
602 
603   DCHECK(pending_cursor_);
604   SetResultCursor(pending_cursor_.Release(), std::move(key),
605                   std::move(primary_key), std::move(value));
606   metrics_.RecordAndReset();
607 }
608 
HasPendingActivity() const609 bool IDBRequest::HasPendingActivity() const {
610   // FIXME: In an ideal world, we should return true as long as anyone has a or
611   //        can get a handle to us and we have event listeners. This is order to
612   //        handle user generated events properly.
613   return has_pending_activity_ && GetExecutionContext();
614 }
615 
ContextDestroyed()616 void IDBRequest::ContextDestroyed() {
617   if (ready_state_ == PENDING) {
618     ready_state_ = kEarlyDeath;
619     if (queue_item_)
620       queue_item_->CancelLoading();
621     if (transaction_)
622       transaction_->UnregisterRequest(this);
623   }
624 
625   if (source_.IsIDBCursor())
626     source_.GetAsIDBCursor()->ContextWillBeDestroyed();
627   if (result_)
628     result_->ContextWillBeDestroyed();
629   if (pending_cursor_)
630     pending_cursor_->ContextWillBeDestroyed();
631   if (web_callbacks_) {
632     web_callbacks_->DetachRequestFromCallback();
633     web_callbacks_ = nullptr;
634   }
635 }
636 
InterfaceName() const637 const AtomicString& IDBRequest::InterfaceName() const {
638   return event_target_names::kIDBRequest;
639 }
640 
GetExecutionContext() const641 ExecutionContext* IDBRequest::GetExecutionContext() const {
642   return ExecutionContextLifecycleObserver::GetExecutionContext();
643 }
644 
DispatchEventInternal(Event & event)645 DispatchEventResult IDBRequest::DispatchEventInternal(Event& event) {
646   IDB_TRACE("IDBRequest::dispatchEvent");
647 
648   event.SetTarget(this);
649 
650   HeapVector<Member<EventTarget>> targets;
651   targets.push_back(this);
652   if (transaction_ && !prevent_propagation_) {
653     // Per spec: "A request's get the parent algorithm returns the request’s
654     // transaction."
655     targets.push_back(transaction_);
656     // Per spec: "A transaction's get the parent algorithm returns the
657     // transaction’s connection."
658     targets.push_back(transaction_->db());
659   }
660 
661   // If this event originated from script, it should have no side effects.
662   if (!event.isTrusted())
663     return IDBEventDispatcher::Dispatch(event, targets);
664   DCHECK(event.type() == event_type_names::kSuccess ||
665          event.type() == event_type_names::kError ||
666          event.type() == event_type_names::kBlocked ||
667          event.type() == event_type_names::kUpgradeneeded)
668       << "event type was " << event.type();
669 
670   if (!GetExecutionContext())
671     return DispatchEventResult::kCanceledBeforeDispatch;
672   DCHECK_EQ(ready_state_, PENDING);
673   DCHECK(has_pending_activity_);
674   DCHECK_EQ(event.target(), this);
675 
676   if (event.type() != event_type_names::kBlocked)
677     ready_state_ = DONE;
678 
679   // Cursor properties should not be updated until the success event is being
680   // dispatched.
681   IDBCursor* cursor_to_notify = nullptr;
682   if (event.type() == event_type_names::kSuccess) {
683     cursor_to_notify = GetResultCursor();
684     if (cursor_to_notify) {
685       cursor_to_notify->SetValueReady(std::move(cursor_key_),
686                                       std::move(cursor_primary_key_),
687                                       std::move(cursor_value_));
688     }
689   }
690 
691   if (event.type() == event_type_names::kUpgradeneeded) {
692     DCHECK(!did_fire_upgrade_needed_event_);
693     did_fire_upgrade_needed_event_ = true;
694   }
695 
696   const bool set_transaction_active =
697       transaction_ &&
698       (event.type() == event_type_names::kSuccess ||
699        event.type() == event_type_names::kUpgradeneeded ||
700        (event.type() == event_type_names::kError && !request_aborted_));
701 
702   if (set_transaction_active)
703     transaction_->SetActive(true);
704 
705   // The request must be unregistered from the transaction before the event
706   // handler is invoked, because the handler can call an IDBCursor method that
707   // reuses this request, like continue() or advance(). http://crbug.com/724109
708   // describes the consequences of getting this wrong.
709   if (transaction_ && ready_state_ == DONE)
710     transaction_->UnregisterRequest(this);
711 
712   if (event.type() == event_type_names::kError && transaction_)
713     transaction_->IncrementNumErrorsHandled();
714 
715   // Now that the event dispatching has been triggered, record that the metric
716   // has completed.
717   metrics_.RecordAndReset();
718 
719   DispatchEventResult dispatch_result =
720       IDBEventDispatcher::Dispatch(event, targets);
721 
722   if (transaction_) {
723     // Possibly abort the transaction. This must occur after unregistering (so
724     // this request doesn't receive a second error) and before deactivating
725     // (which might trigger commit).
726     if (!request_aborted_) {
727       // Transactions should be aborted after event dispatch if an exception was
728       // not caught.
729       if (event.LegacyDidListenersThrow()) {
730         transaction_->SetError(MakeGarbageCollected<DOMException>(
731             DOMExceptionCode::kAbortError,
732             "Uncaught exception in event handler."));
733         transaction_->abort(IGNORE_EXCEPTION_FOR_TESTING);
734       } else if (event.type() == event_type_names::kError &&
735                  dispatch_result == DispatchEventResult::kNotCanceled) {
736         transaction_->SetError(error_);
737         transaction_->abort(IGNORE_EXCEPTION_FOR_TESTING);
738       }
739     }
740 
741     // If this was the last request in the transaction's list, it may commit
742     // here.
743     if (set_transaction_active)
744       transaction_->SetActive(false);
745   }
746 
747   if (cursor_to_notify)
748     cursor_to_notify->PostSuccessHandlerCallback();
749 
750   // An upgradeneeded event will always be followed by a success or error event,
751   // so must be kept alive.
752   if (ready_state_ == DONE && event.type() != event_type_names::kUpgradeneeded)
753     has_pending_activity_ = false;
754 
755   return dispatch_result;
756 }
757 
TransactionDidFinishAndDispatch()758 void IDBRequest::TransactionDidFinishAndDispatch() {
759   DCHECK(transaction_);
760   DCHECK(transaction_->IsVersionChange());
761   DCHECK(did_fire_upgrade_needed_event_);
762   DCHECK_EQ(ready_state_, DONE);
763   DCHECK(GetExecutionContext());
764   transaction_.Clear();
765 
766   if (!GetExecutionContext())
767     return;
768 
769   ready_state_ = PENDING;
770 }
771 
EnqueueEvent(Event * event)772 void IDBRequest::EnqueueEvent(Event* event) {
773   DCHECK(ready_state_ == PENDING || ready_state_ == DONE);
774 
775   if (!GetExecutionContext())
776     return;
777 
778   DCHECK(ready_state_ == PENDING || did_fire_upgrade_needed_event_)
779       << "When queueing event " << event->type() << ", ready_state_ was "
780       << ready_state_;
781 
782   event->SetTarget(this);
783 
784   event_queue_->EnqueueEvent(FROM_HERE, *event);
785 }
786 
787 }  // namespace blink
788