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