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