1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "third_party/blink/renderer/modules/indexeddb/idb_request_queue_item.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include "base/callback.h"
11 #include "base/memory/scoped_refptr.h"
12 #include "mojo/public/cpp/bindings/receiver.h"
13 #include "third_party/blink/renderer/core/dom/dom_exception.h"
14 #include "third_party/blink/renderer/modules/indexeddb/idb_key.h"
15 #include "third_party/blink/renderer/modules/indexeddb/idb_request.h"
16 #include "third_party/blink/renderer/modules/indexeddb/idb_request_loader.h"
17 #include "third_party/blink/renderer/modules/indexeddb/idb_value.h"
18 #include "third_party/blink/renderer/modules/indexeddb/idb_value_wrapping.h"
19 #include "third_party/blink/renderer/modules/indexeddb/web_idb_cursor.h"
20
21 namespace blink {
22
23 class IDBDatabaseGetAllResultSinkImpl
24 : public mojom::blink::IDBDatabaseGetAllResultSink {
25 public:
IDBDatabaseGetAllResultSinkImpl(mojo::PendingReceiver<mojom::blink::IDBDatabaseGetAllResultSink> receiver,IDBRequestQueueItem * owner,bool key_only)26 IDBDatabaseGetAllResultSinkImpl(
27 mojo::PendingReceiver<mojom::blink::IDBDatabaseGetAllResultSink> receiver,
28 IDBRequestQueueItem* owner,
29 bool key_only)
30 : receiver_(this, std::move(receiver)),
31 owner_(owner),
32 key_only_(key_only) {
33 receiver_.set_disconnect_handler(WTF::Bind(
34 &IDBDatabaseGetAllResultSinkImpl::OnDisconnect, WTF::Unretained(this)));
35 }
36
37 ~IDBDatabaseGetAllResultSinkImpl() override = default;
38
IsWaiting() const39 bool IsWaiting() const { return receiver_.is_bound(); }
40
OnDisconnect()41 void OnDisconnect() {
42 // Force IsWaiting to be false.
43 receiver_.reset();
44
45 if (key_only_) {
46 owner_->response_type_ = IDBRequestQueueItem::kKey;
47 owner_->key_ = IDBKey::CreateArray(std::move(keys_));
48 owner_->OnResultLoadComplete();
49 } else {
50 owner_->response_type_ = IDBRequestQueueItem::kValueArray;
51
52 Vector<std::unique_ptr<IDBValue>> idb_values;
53 idb_values.ReserveInitialCapacity(values_.size());
54 for (const mojom::blink::IDBReturnValuePtr& value : values_) {
55 std::unique_ptr<IDBValue> idb_value =
56 IDBValue::ConvertReturnValue(value);
57 idb_value->SetIsolate(owner_->request_->GetIsolate());
58 idb_values.emplace_back(std::move(idb_value));
59 }
60
61 bool is_wrapped = IDBValueUnwrapper::IsWrapped(idb_values);
62 owner_->values_ = std::move(idb_values);
63 if (is_wrapped) {
64 owner_->loader_ =
65 std::make_unique<IDBRequestLoader>(owner_, owner_->values_);
66 if (owner_->started_loading_) {
67 // Try again now that the values exist.
68 owner_->StartLoading();
69 }
70 } else {
71 owner_->OnResultLoadComplete();
72 }
73 }
74 }
75
ReceiveValues(WTF::Vector<mojom::blink::IDBReturnValuePtr> values)76 void ReceiveValues(
77 WTF::Vector<mojom::blink::IDBReturnValuePtr> values) override {
78 DCHECK(!key_only_);
79 DCHECK_LE(values.size(),
80 static_cast<wtf_size_t>(mojom::blink::kIDBGetAllChunkSize));
81 if (values_.IsEmpty()) {
82 values_ = std::move(values);
83 return;
84 }
85
86 values_.ReserveCapacity(values_.size() + values.size());
87 for (auto& value : values)
88 values_.emplace_back(std::move(value));
89 }
90
ReceiveKeys(WTF::Vector<std::unique_ptr<IDBKey>> keys)91 void ReceiveKeys(WTF::Vector<std::unique_ptr<IDBKey>> keys) override {
92 DCHECK(key_only_);
93 DCHECK_LE(keys.size(),
94 static_cast<wtf_size_t>(mojom::blink::kIDBGetAllChunkSize));
95 if (keys_.IsEmpty()) {
96 keys_ = std::move(keys);
97 return;
98 }
99
100 keys_.ReserveCapacity(keys_.size() + keys.size());
101 for (auto& key : keys)
102 keys_.emplace_back(std::move(key));
103 }
104
OnError(mojom::blink::IDBErrorPtr error)105 void OnError(mojom::blink::IDBErrorPtr error) override {
106 owner_->response_type_ = IDBRequestQueueItem::kError;
107 owner_->error_ = MakeGarbageCollected<DOMException>(
108 static_cast<DOMExceptionCode>(error->error_code), error->error_message);
109 // Prevent OnDisconnect from sending keys or values.
110 receiver_.reset();
111 owner_->OnResultLoadComplete();
112 }
113
114 private:
115 mojo::Receiver<mojom::blink::IDBDatabaseGetAllResultSink> receiver_;
116 IDBRequestQueueItem* owner_;
117 bool key_only_;
118
119 WTF::Vector<mojom::blink::IDBReturnValuePtr> values_;
120 WTF::Vector<std::unique_ptr<IDBKey>> keys_;
121 };
122
IDBRequestQueueItem(IDBRequest * request,DOMException * error,base::OnceClosure on_result_load_complete)123 IDBRequestQueueItem::IDBRequestQueueItem(
124 IDBRequest* request,
125 DOMException* error,
126 base::OnceClosure on_result_load_complete)
127 : request_(request),
128 error_(error),
129 on_result_load_complete_(std::move(on_result_load_complete)),
130 response_type_(kError),
131 ready_(true) {
132 DCHECK(on_result_load_complete_);
133 DCHECK_EQ(request->queue_item_, nullptr);
134 request_->queue_item_ = this;
135 }
136
IDBRequestQueueItem(IDBRequest * request,int64_t value,base::OnceClosure on_result_load_complete)137 IDBRequestQueueItem::IDBRequestQueueItem(
138 IDBRequest* request,
139 int64_t value,
140 base::OnceClosure on_result_load_complete)
141 : request_(request),
142 on_result_load_complete_(std::move(on_result_load_complete)),
143 int64_value_(value),
144 response_type_(kNumber),
145 ready_(true) {
146 DCHECK(on_result_load_complete_);
147 DCHECK_EQ(request->queue_item_, nullptr);
148 request_->queue_item_ = this;
149 }
150
IDBRequestQueueItem(IDBRequest * request,base::OnceClosure on_result_load_complete)151 IDBRequestQueueItem::IDBRequestQueueItem(
152 IDBRequest* request,
153 base::OnceClosure on_result_load_complete)
154 : request_(request),
155 on_result_load_complete_(std::move(on_result_load_complete)),
156 response_type_(kVoid),
157 ready_(true) {
158 DCHECK(on_result_load_complete_);
159 DCHECK_EQ(request->queue_item_, nullptr);
160 request_->queue_item_ = this;
161 }
162
IDBRequestQueueItem(IDBRequest * request,std::unique_ptr<IDBKey> key,base::OnceClosure on_result_load_complete)163 IDBRequestQueueItem::IDBRequestQueueItem(
164 IDBRequest* request,
165 std::unique_ptr<IDBKey> key,
166 base::OnceClosure on_result_load_complete)
167 : request_(request),
168 key_(std::move(key)),
169 on_result_load_complete_(std::move(on_result_load_complete)),
170 response_type_(kKey),
171 ready_(true) {
172 DCHECK(on_result_load_complete_);
173 DCHECK_EQ(request->queue_item_, nullptr);
174 request_->queue_item_ = this;
175 }
176
IDBRequestQueueItem(IDBRequest * request,std::unique_ptr<IDBValue> value,bool attach_loader,base::OnceClosure on_result_load_complete)177 IDBRequestQueueItem::IDBRequestQueueItem(
178 IDBRequest* request,
179 std::unique_ptr<IDBValue> value,
180 bool attach_loader,
181 base::OnceClosure on_result_load_complete)
182 : request_(request),
183 on_result_load_complete_(std::move(on_result_load_complete)),
184 response_type_(kValue),
185 ready_(!attach_loader) {
186 DCHECK(on_result_load_complete_);
187 DCHECK_EQ(request->queue_item_, nullptr);
188 request_->queue_item_ = this;
189 values_.push_back(std::move(value));
190 if (attach_loader)
191 loader_ = std::make_unique<IDBRequestLoader>(this, values_);
192 }
193
IDBRequestQueueItem(IDBRequest * request,Vector<std::unique_ptr<IDBValue>> values,bool attach_loader,base::OnceClosure on_result_load_complete)194 IDBRequestQueueItem::IDBRequestQueueItem(
195 IDBRequest* request,
196 Vector<std::unique_ptr<IDBValue>> values,
197 bool attach_loader,
198 base::OnceClosure on_result_load_complete)
199 : request_(request),
200 values_(std::move(values)),
201 on_result_load_complete_(std::move(on_result_load_complete)),
202 response_type_(kValueArray),
203 ready_(!attach_loader) {
204 DCHECK(on_result_load_complete_);
205 DCHECK_EQ(request->queue_item_, nullptr);
206 request_->queue_item_ = this;
207 if (attach_loader)
208 loader_ = std::make_unique<IDBRequestLoader>(this, values_);
209 }
210
IDBRequestQueueItem(IDBRequest * request,std::unique_ptr<IDBKey> key,std::unique_ptr<IDBKey> primary_key,std::unique_ptr<IDBValue> value,bool attach_loader,base::OnceClosure on_result_load_complete)211 IDBRequestQueueItem::IDBRequestQueueItem(
212 IDBRequest* request,
213 std::unique_ptr<IDBKey> key,
214 std::unique_ptr<IDBKey> primary_key,
215 std::unique_ptr<IDBValue> value,
216 bool attach_loader,
217 base::OnceClosure on_result_load_complete)
218 : request_(request),
219 key_(std::move(key)),
220 primary_key_(std::move(primary_key)),
221 on_result_load_complete_(std::move(on_result_load_complete)),
222 response_type_(kKeyPrimaryKeyValue),
223 ready_(!attach_loader) {
224 DCHECK(on_result_load_complete_);
225 DCHECK_EQ(request->queue_item_, nullptr);
226 request_->queue_item_ = this;
227 values_.push_back(std::move(value));
228 if (attach_loader)
229 loader_ = std::make_unique<IDBRequestLoader>(this, values_);
230 }
231
IDBRequestQueueItem(IDBRequest * request,std::unique_ptr<WebIDBCursor> cursor,std::unique_ptr<IDBKey> key,std::unique_ptr<IDBKey> primary_key,std::unique_ptr<IDBValue> value,bool attach_loader,base::OnceClosure on_result_load_complete)232 IDBRequestQueueItem::IDBRequestQueueItem(
233 IDBRequest* request,
234 std::unique_ptr<WebIDBCursor> cursor,
235 std::unique_ptr<IDBKey> key,
236 std::unique_ptr<IDBKey> primary_key,
237 std::unique_ptr<IDBValue> value,
238 bool attach_loader,
239 base::OnceClosure on_result_load_complete)
240 : request_(request),
241 key_(std::move(key)),
242 primary_key_(std::move(primary_key)),
243 cursor_(std::move(cursor)),
244 on_result_load_complete_(std::move(on_result_load_complete)),
245 response_type_(kCursorKeyPrimaryKeyValue),
246 ready_(!attach_loader) {
247 DCHECK(on_result_load_complete_);
248 DCHECK_EQ(request->queue_item_, nullptr);
249 request_->queue_item_ = this;
250 values_.push_back(std::move(value));
251 if (attach_loader)
252 loader_ = std::make_unique<IDBRequestLoader>(this, values_);
253 }
254
IDBRequestQueueItem(IDBRequest * request,bool key_only,mojo::PendingReceiver<mojom::blink::IDBDatabaseGetAllResultSink> receiver,base::OnceClosure on_result_load_complete)255 IDBRequestQueueItem::IDBRequestQueueItem(
256 IDBRequest* request,
257 bool key_only,
258 mojo::PendingReceiver<mojom::blink::IDBDatabaseGetAllResultSink> receiver,
259 base::OnceClosure on_result_load_complete)
260 : request_(request),
261 on_result_load_complete_(std::move(on_result_load_complete)),
262 ready_(false) {
263 DCHECK_EQ(request->queue_item_, nullptr);
264 request_->queue_item_ = this;
265 get_all_sink_ = std::make_unique<IDBDatabaseGetAllResultSinkImpl>(
266 std::move(receiver), this, key_only);
267 }
268
~IDBRequestQueueItem()269 IDBRequestQueueItem::~IDBRequestQueueItem() {
270 #if DCHECK_IS_ON()
271 DCHECK(ready_);
272 DCHECK(callback_fired_);
273 #endif // DCHECK_IS_ON()
274 }
275
OnResultLoadComplete()276 void IDBRequestQueueItem::OnResultLoadComplete() {
277 DCHECK(!ready_);
278 ready_ = true;
279
280 DCHECK(on_result_load_complete_);
281 std::move(on_result_load_complete_).Run();
282 }
283
OnResultLoadComplete(DOMException * error)284 void IDBRequestQueueItem::OnResultLoadComplete(DOMException* error) {
285 DCHECK(!ready_);
286 DCHECK(response_type_ != kError);
287
288 response_type_ = kError;
289 error_ = error;
290
291 // This is not necessary, but releases non-trivial amounts of memory early.
292 values_.clear();
293
294 OnResultLoadComplete();
295 }
296
StartLoading()297 void IDBRequestQueueItem::StartLoading() {
298 started_loading_ = true;
299
300 // If waiting on results from get all before loading, early out.
301 if (get_all_sink_ && get_all_sink_->IsWaiting())
302 return;
303
304 if (request_->request_aborted_) {
305 // The backing store can get the result back to the request after it's been
306 // aborted due to a transaction abort. In this case, we can't rely on
307 // IDBRequest::Abort() to call CancelLoading().
308
309 // Setting loader_ to null here makes sure we don't call Cancel() on a
310 // IDBRequestLoader that hasn't been Start()ed. The current implementation
311 // behaves well even if Cancel() is called without Start() being called, but
312 // this reset makes the IDBRequestLoader lifecycle easier to reason about.
313 loader_.reset();
314
315 CancelLoading();
316 return;
317 }
318
319 if (loader_) {
320 DCHECK(!ready_);
321 loader_->Start();
322 }
323 }
324
CancelLoading()325 void IDBRequestQueueItem::CancelLoading() {
326 if (ready_)
327 return;
328
329 if (get_all_sink_)
330 get_all_sink_.reset();
331
332 if (loader_) {
333 loader_->Cancel();
334 loader_.reset();
335
336 // IDBRequestLoader::Cancel() should not call any of the EnqueueResponse
337 // variants.
338 DCHECK(!ready_);
339 }
340
341 // Mark this item as ready so the transaction's result queue can be drained.
342 response_type_ = kCanceled;
343 values_.clear();
344 OnResultLoadComplete();
345 }
346
EnqueueResponse()347 void IDBRequestQueueItem::EnqueueResponse() {
348 DCHECK(ready_);
349 #if DCHECK_IS_ON()
350 DCHECK(!callback_fired_);
351 callback_fired_ = true;
352 #endif // DCHECK_IS_ON()
353 DCHECK_EQ(request_->queue_item_, this);
354 request_->queue_item_ = nullptr;
355
356 switch (response_type_) {
357 case kCanceled:
358 DCHECK_EQ(values_.size(), 0U);
359 break;
360
361 case kCursorKeyPrimaryKeyValue:
362 DCHECK_EQ(values_.size(), 1U);
363 request_->EnqueueResponse(std::move(cursor_), std::move(key_),
364 std::move(primary_key_),
365 std::move(values_.front()));
366 break;
367
368 case kError:
369 DCHECK(error_);
370 request_->EnqueueResponse(error_);
371 break;
372
373 case kKeyPrimaryKeyValue:
374 DCHECK_EQ(values_.size(), 1U);
375 request_->EnqueueResponse(std::move(key_), std::move(primary_key_),
376 std::move(values_.front()));
377 break;
378
379 case kKey:
380 DCHECK_EQ(values_.size(), 0U);
381 request_->EnqueueResponse(std::move(key_));
382 break;
383
384 case kNumber:
385 DCHECK_EQ(values_.size(), 0U);
386 request_->EnqueueResponse(int64_value_);
387 break;
388
389 case kValue:
390 DCHECK_EQ(values_.size(), 1U);
391 request_->EnqueueResponse(std::move(values_.front()));
392 break;
393
394 case kValueArray:
395 request_->EnqueueResponse(std::move(values_));
396 break;
397
398 case kVoid:
399 DCHECK_EQ(values_.size(), 0U);
400 request_->EnqueueResponse();
401 break;
402 }
403 }
404
405 } // namespace blink
406