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