1 // Copyright 2013 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/web_idb_cursor_impl.h"
6 
7 #include <stddef.h>
8 
9 #include "base/single_thread_task_runner.h"
10 #include "mojo/public/cpp/bindings/self_owned_associated_receiver.h"
11 #include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-blink.h"
12 #include "third_party/blink/renderer/modules/indexeddb/idb_key_range.h"
13 #include "third_party/blink/renderer/modules/indexeddb/indexed_db_dispatcher.h"
14 #include "third_party/blink/renderer/platform/wtf/functional.h"
15 
16 namespace blink {
17 
WebIDBCursorImpl(mojo::PendingAssociatedRemote<mojom::blink::IDBCursor> cursor_info,int64_t transaction_id,scoped_refptr<base::SingleThreadTaskRunner> task_runner)18 WebIDBCursorImpl::WebIDBCursorImpl(
19     mojo::PendingAssociatedRemote<mojom::blink::IDBCursor> cursor_info,
20     int64_t transaction_id,
21     scoped_refptr<base::SingleThreadTaskRunner> task_runner)
22     : transaction_id_(transaction_id),
23       continue_count_(0),
24       used_prefetches_(0),
25       pending_onsuccess_callbacks_(0),
26       prefetch_amount_(kMinPrefetchAmount),
27       task_runner_(task_runner) {
28   cursor_.Bind(std::move(cursor_info), std::move(task_runner));
29   IndexedDBDispatcher::RegisterCursor(this);
30 }
31 
~WebIDBCursorImpl()32 WebIDBCursorImpl::~WebIDBCursorImpl() {
33   // It's not possible for there to be pending callbacks that address this
34   // object since inside WebKit, they hold a reference to the object which owns
35   // this object. But, if that ever changed, then we'd need to invalidate
36   // any such pointers.
37   IndexedDBDispatcher::UnregisterCursor(this);
38 }
39 
Advance(uint32_t count,WebIDBCallbacks * callbacks_ptr)40 void WebIDBCursorImpl::Advance(uint32_t count, WebIDBCallbacks* callbacks_ptr) {
41   std::unique_ptr<WebIDBCallbacks> callbacks(callbacks_ptr);
42   if (count <= prefetch_keys_.size()) {
43     CachedAdvance(count, callbacks.get());
44     return;
45   }
46   ResetPrefetchCache();
47 
48   // Reset all cursor prefetch caches except for this cursor.
49   IndexedDBDispatcher::ResetCursorPrefetchCaches(transaction_id_, this);
50 
51   callbacks->SetState(weak_factory_.GetWeakPtr(), transaction_id_);
52   cursor_->Advance(count,
53                    WTF::Bind(&WebIDBCursorImpl::AdvanceCallback,
54                              WTF::Unretained(this), std::move(callbacks)));
55 }
56 
AdvanceCallback(std::unique_ptr<WebIDBCallbacks> callbacks,mojom::blink::IDBCursorResultPtr result)57 void WebIDBCursorImpl::AdvanceCallback(
58     std::unique_ptr<WebIDBCallbacks> callbacks,
59     mojom::blink::IDBCursorResultPtr result) {
60   if (result->is_error_result()) {
61     callbacks->Error(result->get_error_result()->error_code,
62                      std::move(result->get_error_result()->error_message));
63     callbacks.reset();
64     return;
65   }
66 
67   if (result->is_empty() && result->get_empty()) {
68     callbacks->SuccessValue(nullptr);
69     callbacks.reset();
70     return;
71   } else if (result->is_empty()) {
72     callbacks->Error(blink::mojom::IDBException::kUnknownError,
73                      "Invalid response");
74     callbacks.reset();
75     return;
76   }
77 
78   if (result->get_values()->keys.size() != 1u ||
79       result->get_values()->primary_keys.size() != 1u ||
80       result->get_values()->values.size() != 1u) {
81     callbacks->Error(blink::mojom::IDBException::kUnknownError,
82                      "Invalid response");
83     callbacks.reset();
84     return;
85   }
86 
87   callbacks->SuccessCursorContinue(
88       std::move(result->get_values()->keys[0]),
89       std::move(result->get_values()->primary_keys[0]),
90       std::move(result->get_values()->values[0]));
91   callbacks.reset();
92 }
93 
CursorContinue(const IDBKey * key,const IDBKey * primary_key,WebIDBCallbacks * callbacks_ptr)94 void WebIDBCursorImpl::CursorContinue(const IDBKey* key,
95                                       const IDBKey* primary_key,
96                                       WebIDBCallbacks* callbacks_ptr) {
97   DCHECK(key && primary_key);
98   std::unique_ptr<WebIDBCallbacks> callbacks(callbacks_ptr);
99 
100   if (key->GetType() == mojom::IDBKeyType::None &&
101       primary_key->GetType() == mojom::IDBKeyType::None) {
102     // No key(s), so this would qualify for a prefetch.
103     ++continue_count_;
104 
105     if (!prefetch_keys_.IsEmpty()) {
106       // We have a prefetch cache, so serve the result from that.
107       CachedContinue(callbacks.get());
108       return;
109     }
110 
111     if (continue_count_ > kPrefetchContinueThreshold) {
112       // Request pre-fetch.
113       ++pending_onsuccess_callbacks_;
114 
115       callbacks->SetState(weak_factory_.GetWeakPtr(), transaction_id_);
116       cursor_->Prefetch(prefetch_amount_,
117                         WTF::Bind(&WebIDBCursorImpl::PrefetchCallback,
118                                   WTF::Unretained(this), std::move(callbacks)));
119 
120       // Increase prefetch_amount_ exponentially.
121       prefetch_amount_ *= 2;
122       if (prefetch_amount_ > kMaxPrefetchAmount)
123         prefetch_amount_ = kMaxPrefetchAmount;
124 
125       return;
126     }
127   } else {
128     // Key argument supplied. We couldn't prefetch this.
129     ResetPrefetchCache();
130   }
131 
132   // Reset all cursor prefetch caches except for this cursor.
133   IndexedDBDispatcher::ResetCursorPrefetchCaches(transaction_id_, this);
134   callbacks->SetState(weak_factory_.GetWeakPtr(), transaction_id_);
135   cursor_->CursorContinue(
136       IDBKey::Clone(key), IDBKey::Clone(primary_key),
137       WTF::Bind(&WebIDBCursorImpl::CursorContinueCallback,
138                 WTF::Unretained(this), std::move(callbacks)));
139 }
140 
CursorContinueCallback(std::unique_ptr<WebIDBCallbacks> callbacks,mojom::blink::IDBCursorResultPtr result)141 void WebIDBCursorImpl::CursorContinueCallback(
142     std::unique_ptr<WebIDBCallbacks> callbacks,
143     mojom::blink::IDBCursorResultPtr result) {
144   if (result->is_error_result()) {
145     callbacks->Error(result->get_error_result()->error_code,
146                      std::move(result->get_error_result()->error_message));
147     callbacks.reset();
148     return;
149   }
150 
151   if (result->is_empty() && result->get_empty()) {
152     callbacks->SuccessValue(nullptr);
153     callbacks.reset();
154     return;
155   } else if (result->is_empty()) {
156     callbacks->Error(blink::mojom::IDBException::kUnknownError,
157                      "Invalid response");
158     callbacks.reset();
159     return;
160   }
161 
162   if (result->get_values()->keys.size() != 1u ||
163       result->get_values()->primary_keys.size() != 1u ||
164       result->get_values()->values.size() != 1u) {
165     callbacks->Error(blink::mojom::IDBException::kUnknownError,
166                      "Invalid response");
167     callbacks.reset();
168     return;
169   }
170 
171   callbacks->SuccessCursorContinue(
172       std::move(result->get_values()->keys[0]),
173       std::move(result->get_values()->primary_keys[0]),
174       std::move(result->get_values()->values[0]));
175   callbacks.reset();
176 }
177 
PrefetchCallback(std::unique_ptr<WebIDBCallbacks> callbacks,mojom::blink::IDBCursorResultPtr result)178 void WebIDBCursorImpl::PrefetchCallback(
179     std::unique_ptr<WebIDBCallbacks> callbacks,
180     mojom::blink::IDBCursorResultPtr result) {
181   if (result->is_error_result()) {
182     callbacks->Error(result->get_error_result()->error_code,
183                      std::move(result->get_error_result()->error_message));
184     callbacks.reset();
185     return;
186   }
187 
188   if (result->is_empty() && result->get_empty()) {
189     callbacks->SuccessValue(nullptr);
190     callbacks.reset();
191     return;
192   } else if (result->is_empty()) {
193     callbacks->Error(blink::mojom::IDBException::kUnknownError,
194                      "Invalid response");
195     callbacks.reset();
196     return;
197   }
198 
199   if (result->get_values()->keys.size() !=
200           result->get_values()->primary_keys.size() ||
201       result->get_values()->keys.size() !=
202           result->get_values()->values.size()) {
203     callbacks->Error(blink::mojom::IDBException::kUnknownError,
204                      "Invalid response");
205     callbacks.reset();
206     return;
207   }
208 
209   callbacks->SuccessCursorPrefetch(
210       std::move(result->get_values()->keys),
211       std::move(result->get_values()->primary_keys),
212       std::move(result->get_values()->values));
213   callbacks.reset();
214 }
215 
PostSuccessHandlerCallback()216 void WebIDBCursorImpl::PostSuccessHandlerCallback() {
217   pending_onsuccess_callbacks_--;
218 
219   // If the onsuccess callback called continue()/advance() on the cursor
220   // again, and that request was served by the prefetch cache, then
221   // pending_onsuccess_callbacks_ would be incremented. If not, it means the
222   // callback did something else, or nothing at all, in which case we need to
223   // reset the cache.
224 
225   if (pending_onsuccess_callbacks_ == 0)
226     ResetPrefetchCache();
227 }
228 
SetPrefetchData(Vector<std::unique_ptr<IDBKey>> keys,Vector<std::unique_ptr<IDBKey>> primary_keys,Vector<std::unique_ptr<IDBValue>> values)229 void WebIDBCursorImpl::SetPrefetchData(
230     Vector<std::unique_ptr<IDBKey>> keys,
231     Vector<std::unique_ptr<IDBKey>> primary_keys,
232     Vector<std::unique_ptr<IDBValue>> values) {
233   // Keys and values are stored in reverse order so that a cache'd continue can
234   // pop a value off of the back and prevent new memory allocations.
235   prefetch_keys_.AppendRange(std::make_move_iterator(keys.rbegin()),
236                              std::make_move_iterator(keys.rend()));
237   prefetch_primary_keys_.AppendRange(
238       std::make_move_iterator(primary_keys.rbegin()),
239       std::make_move_iterator(primary_keys.rend()));
240   prefetch_values_.AppendRange(std::make_move_iterator(values.rbegin()),
241                                std::make_move_iterator(values.rend()));
242 
243   used_prefetches_ = 0;
244   pending_onsuccess_callbacks_ = 0;
245 }
246 
CachedAdvance(uint32_t count,WebIDBCallbacks * callbacks)247 void WebIDBCursorImpl::CachedAdvance(uint32_t count,
248                                      WebIDBCallbacks* callbacks) {
249   DCHECK_GE(prefetch_keys_.size(), count);
250   DCHECK_EQ(prefetch_primary_keys_.size(), prefetch_keys_.size());
251   DCHECK_EQ(prefetch_values_.size(), prefetch_keys_.size());
252 
253   while (count > 1) {
254     prefetch_keys_.pop_back();
255     prefetch_primary_keys_.pop_back();
256     prefetch_values_.pop_back();
257     ++used_prefetches_;
258     --count;
259   }
260 
261   CachedContinue(callbacks);
262 }
263 
CachedContinue(WebIDBCallbacks * callbacks)264 void WebIDBCursorImpl::CachedContinue(WebIDBCallbacks* callbacks) {
265   DCHECK_GT(prefetch_keys_.size(), 0ul);
266   DCHECK_EQ(prefetch_primary_keys_.size(), prefetch_keys_.size());
267   DCHECK_EQ(prefetch_values_.size(), prefetch_keys_.size());
268 
269   // Keys and values are stored in reverse order so that a cache'd continue can
270   // pop a value off of the back and prevent new memory allocations.
271   std::unique_ptr<IDBKey> key = std::move(prefetch_keys_.back());
272   std::unique_ptr<IDBKey> primary_key =
273       std::move(prefetch_primary_keys_.back());
274   std::unique_ptr<IDBValue> value = std::move(prefetch_values_.back());
275 
276   prefetch_keys_.pop_back();
277   prefetch_primary_keys_.pop_back();
278   prefetch_values_.pop_back();
279   ++used_prefetches_;
280 
281   ++pending_onsuccess_callbacks_;
282 
283   if (!continue_count_) {
284     // The cache was invalidated by a call to ResetPrefetchCache()
285     // after the RequestIDBCursorPrefetch() was made. Now that the
286     // initiating continue() call has been satisfied, discard
287     // the rest of the cache.
288     ResetPrefetchCache();
289   }
290 
291   callbacks->SuccessCursorContinue(std::move(key), std::move(primary_key),
292                                    std::move(value));
293 }
294 
ResetPrefetchCache()295 void WebIDBCursorImpl::ResetPrefetchCache() {
296   continue_count_ = 0;
297   prefetch_amount_ = kMinPrefetchAmount;
298 
299   if (prefetch_keys_.IsEmpty()) {
300     // No prefetch cache, so no need to reset the cursor in the back-end.
301     return;
302   }
303 
304   // Reset the back-end cursor.
305   cursor_->PrefetchReset(used_prefetches_, prefetch_keys_.size());
306 
307   // Reset the prefetch cache.
308   prefetch_keys_.clear();
309   prefetch_primary_keys_.clear();
310   prefetch_values_.clear();
311 
312   pending_onsuccess_callbacks_ = 0;
313 }
314 
315 mojo::PendingAssociatedRemote<mojom::blink::IDBCallbacks>
GetCallbacksProxy(std::unique_ptr<WebIDBCallbacks> callbacks_impl)316 WebIDBCursorImpl::GetCallbacksProxy(
317     std::unique_ptr<WebIDBCallbacks> callbacks_impl) {
318   mojo::PendingAssociatedRemote<mojom::blink::IDBCallbacks> pending_callbacks;
319   mojo::MakeSelfOwnedAssociatedReceiver(
320       std::move(callbacks_impl),
321       pending_callbacks.InitWithNewEndpointAndPassReceiver(), task_runner_);
322   return pending_callbacks;
323 }
324 
325 }  // namespace blink
326