1 // Copyright (c) 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 "content/browser/indexed_db/indexed_db_cursor.h"
6
7 #include <stddef.h>
8 #include <utility>
9 #include <vector>
10
11 #include "base/bind.h"
12 #include "base/logging.h"
13 #include "base/task/post_task.h"
14 #include "content/browser/indexed_db/indexed_db_callback_helpers.h"
15 #include "content/browser/indexed_db/indexed_db_callbacks.h"
16 #include "content/browser/indexed_db/indexed_db_context_impl.h"
17 #include "content/browser/indexed_db/indexed_db_database_error.h"
18 #include "content/browser/indexed_db/indexed_db_tracing.h"
19 #include "content/browser/indexed_db/indexed_db_transaction.h"
20 #include "content/browser/indexed_db/indexed_db_value.h"
21 #include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
22
23 using blink::IndexedDBKey;
24
25 namespace content {
26 namespace {
27 // This should never be script visible: the cursor should either be closed when
28 // it hits the end of the range (and script throws an error before the call
29 // could be made), if the transaction has finished (ditto), or if there's an
30 // incoming request from the front end but the transaction has aborted on the
31 // back end; in that case the tx will already have sent an abort to the request
32 // so this would be ignored.
CreateCursorClosedError()33 IndexedDBDatabaseError CreateCursorClosedError() {
34 return IndexedDBDatabaseError(blink::mojom::IDBException::kUnknownError,
35 "The cursor has been closed.");
36 }
37
CreateError(blink::mojom::IDBException code,const char * message,base::WeakPtr<IndexedDBTransaction> transaction)38 IndexedDBDatabaseError CreateError(
39 blink::mojom::IDBException code,
40 const char* message,
41 base::WeakPtr<IndexedDBTransaction> transaction) {
42 if (transaction)
43 transaction->IncrementNumErrorsSent();
44 return IndexedDBDatabaseError(code, message);
45 }
46
47 } // namespace
48
IndexedDBCursor(std::unique_ptr<IndexedDBBackingStore::Cursor> cursor,indexed_db::CursorType cursor_type,blink::mojom::IDBTaskType task_type,base::WeakPtr<IndexedDBTransaction> transaction)49 IndexedDBCursor::IndexedDBCursor(
50 std::unique_ptr<IndexedDBBackingStore::Cursor> cursor,
51 indexed_db::CursorType cursor_type,
52 blink::mojom::IDBTaskType task_type,
53 base::WeakPtr<IndexedDBTransaction> transaction)
54 : origin_(
55 transaction->BackingStoreTransaction()->backing_store()->origin()),
56 task_type_(task_type),
57 cursor_type_(cursor_type),
58 transaction_(std::move(transaction)),
59 cursor_(std::move(cursor)),
60 closed_(false) {
61 IDB_ASYNC_TRACE_BEGIN("IndexedDBCursor::open", this);
62 }
63
~IndexedDBCursor()64 IndexedDBCursor::~IndexedDBCursor() {
65 // Call to make sure we complete our lifetime trace.
66 Close();
67 }
68
Advance(uint32_t count,base::WeakPtr<content::IndexedDBDispatcherHost> dispatcher_host,blink::mojom::IDBCursor::AdvanceCallback callback)69 void IndexedDBCursor::Advance(
70 uint32_t count,
71 base::WeakPtr<content::IndexedDBDispatcherHost> dispatcher_host,
72 blink::mojom::IDBCursor::AdvanceCallback callback) {
73 IDB_TRACE("IndexedDBCursor::Advance");
74
75 if (!transaction_)
76 Close();
77 if (closed_) {
78 const IndexedDBDatabaseError error(CreateCursorClosedError());
79 std::move(callback).Run(blink::mojom::IDBCursorResult::NewErrorResult(
80 blink::mojom::IDBError::New(error.code(), error.message())));
81 return;
82 }
83
84 blink::mojom::IDBCursor::AdvanceCallback aborting_callback =
85 CreateCallbackAbortOnDestruct<blink::mojom::IDBCursor::AdvanceCallback,
86 blink::mojom::IDBCursorResultPtr>(
87 std::move(callback), transaction_);
88
89 transaction_->ScheduleTask(
90 task_type_,
91 BindWeakOperation<IndexedDBCursor>(
92 &IndexedDBCursor::CursorAdvanceOperation, ptr_factory_.GetWeakPtr(),
93 count, std::move(dispatcher_host), std::move(aborting_callback)));
94 }
95
CursorAdvanceOperation(uint32_t count,base::WeakPtr<IndexedDBDispatcherHost> dispatcher_host,blink::mojom::IDBCursor::AdvanceCallback callback,IndexedDBTransaction *)96 leveldb::Status IndexedDBCursor::CursorAdvanceOperation(
97 uint32_t count,
98 base::WeakPtr<IndexedDBDispatcherHost> dispatcher_host,
99 blink::mojom::IDBCursor::AdvanceCallback callback,
100 IndexedDBTransaction* /*transaction*/) {
101 IDB_TRACE("IndexedDBCursor::CursorAdvanceOperation");
102 leveldb::Status s = leveldb::Status::OK();
103 if (!dispatcher_host)
104 return s;
105
106 if (!cursor_ || !cursor_->Advance(count, &s)) {
107 cursor_.reset();
108
109 if (s.ok()) {
110 std::move(callback).Run(blink::mojom::IDBCursorResult::NewEmpty(true));
111 return s;
112 }
113
114 // CreateError() needs to be called before calling Close() so
115 // |transaction_| is alive.
116 auto error = CreateError(blink::mojom::IDBException::kUnknownError,
117 "Error advancing cursor", transaction_);
118 Close();
119 std::move(callback).Run(blink::mojom::IDBCursorResult::NewErrorResult(
120 blink::mojom::IDBError::New(error.code(), error.message())));
121 return s;
122 }
123
124 blink::mojom::IDBValuePtr mojo_value;
125 std::vector<IndexedDBExternalObject> external_objects;
126 IndexedDBValue* value = Value();
127 if (value) {
128 mojo_value = IndexedDBValue::ConvertAndEraseValue(value);
129 external_objects.swap(value->external_objects);
130 dispatcher_host->CreateAllExternalObjects(origin_, external_objects,
131 &mojo_value->external_objects);
132 } else {
133 mojo_value = blink::mojom::IDBValue::New();
134 }
135
136 std::vector<IndexedDBKey> keys = {key()};
137 std::vector<IndexedDBKey> primary_keys = {primary_key()};
138 std::vector<blink::mojom::IDBValuePtr> values;
139 values.push_back(std::move(mojo_value));
140 std::move(callback).Run(blink::mojom::IDBCursorResult::NewValues(
141 blink::mojom::IDBCursorValue::New(
142 std::move(keys), std::move(primary_keys), std::move(values))));
143 return s;
144 }
145
Continue(base::WeakPtr<content::IndexedDBDispatcherHost> dispatcher_host,std::unique_ptr<IndexedDBKey> key,std::unique_ptr<IndexedDBKey> primary_key,blink::mojom::IDBCursor::CursorContinueCallback callback)146 void IndexedDBCursor::Continue(
147 base::WeakPtr<content::IndexedDBDispatcherHost> dispatcher_host,
148 std::unique_ptr<IndexedDBKey> key,
149 std::unique_ptr<IndexedDBKey> primary_key,
150 blink::mojom::IDBCursor::CursorContinueCallback callback) {
151 IDB_TRACE("IndexedDBCursor::Continue");
152 if (!transaction_)
153 Close();
154 if (closed_) {
155 const IndexedDBDatabaseError error(CreateCursorClosedError());
156 std::move(callback).Run(blink::mojom::IDBCursorResult::NewErrorResult(
157 blink::mojom::IDBError::New(error.code(), error.message())));
158 return;
159 }
160
161 blink::mojom::IDBCursor::CursorContinueCallback aborting_callback =
162 CreateCallbackAbortOnDestruct<
163 blink::mojom::IDBCursor::CursorContinueCallback,
164 blink::mojom::IDBCursorResultPtr>(std::move(callback), transaction_);
165
166 transaction_->ScheduleTask(
167 task_type_,
168 BindWeakOperation<IndexedDBCursor>(
169 &IndexedDBCursor::CursorContinueOperation, ptr_factory_.GetWeakPtr(),
170 std::move(dispatcher_host), base::Passed(&key),
171 base::Passed(&primary_key), std::move(aborting_callback)));
172 }
173
CursorContinueOperation(base::WeakPtr<IndexedDBDispatcherHost> dispatcher_host,std::unique_ptr<IndexedDBKey> key,std::unique_ptr<IndexedDBKey> primary_key,blink::mojom::IDBCursor::CursorContinueCallback callback,IndexedDBTransaction *)174 leveldb::Status IndexedDBCursor::CursorContinueOperation(
175 base::WeakPtr<IndexedDBDispatcherHost> dispatcher_host,
176 std::unique_ptr<IndexedDBKey> key,
177 std::unique_ptr<IndexedDBKey> primary_key,
178 blink::mojom::IDBCursor::CursorContinueCallback callback,
179 IndexedDBTransaction* /*transaction*/) {
180 IDB_TRACE("IndexedDBCursor::CursorContinueOperation");
181 leveldb::Status s = leveldb::Status::OK();
182 if (!dispatcher_host)
183 return s;
184
185 if (!cursor_ || !cursor_->Continue(key.get(), primary_key.get(),
186 IndexedDBBackingStore::Cursor::SEEK, &s)) {
187 cursor_.reset();
188 if (s.ok()) {
189 // This happens if we reach the end of the iterator and can't continue.
190 std::move(callback).Run(blink::mojom::IDBCursorResult::NewEmpty(true));
191 return s;
192 }
193
194 // |transaction_| must be valid for CreateError(), so we can't call
195 // Close() until after calling CreateError().
196 IndexedDBDatabaseError error =
197 CreateError(blink::mojom::IDBException::kUnknownError,
198 "Error continuing cursor.", transaction_);
199 Close();
200 std::move(callback).Run(blink::mojom::IDBCursorResult::NewErrorResult(
201 blink::mojom::IDBError::New(error.code(), error.message())));
202 return s;
203 }
204
205 blink::mojom::IDBValuePtr mojo_value;
206 std::vector<IndexedDBExternalObject> external_objects;
207 IndexedDBValue* value = Value();
208 if (value) {
209 mojo_value = IndexedDBValue::ConvertAndEraseValue(value);
210 external_objects.swap(value->external_objects);
211 dispatcher_host->CreateAllExternalObjects(origin_, external_objects,
212 &mojo_value->external_objects);
213 } else {
214 mojo_value = blink::mojom::IDBValue::New();
215 }
216
217 std::vector<IndexedDBKey> keys = {this->key()};
218 std::vector<IndexedDBKey> primary_keys = {this->primary_key()};
219 std::vector<blink::mojom::IDBValuePtr> values;
220 values.push_back(std::move(mojo_value));
221 std::move(callback).Run(blink::mojom::IDBCursorResult::NewValues(
222 blink::mojom::IDBCursorValue::New(
223 std::move(keys), std::move(primary_keys), std::move(values))));
224 return s;
225 }
226
PrefetchContinue(base::WeakPtr<IndexedDBDispatcherHost> dispatcher_host,int number_to_fetch,blink::mojom::IDBCursor::PrefetchCallback callback)227 void IndexedDBCursor::PrefetchContinue(
228 base::WeakPtr<IndexedDBDispatcherHost> dispatcher_host,
229 int number_to_fetch,
230 blink::mojom::IDBCursor::PrefetchCallback callback) {
231 IDB_TRACE("IndexedDBCursor::PrefetchContinue");
232
233 if (!transaction_)
234 Close();
235 if (closed_) {
236 const IndexedDBDatabaseError error(CreateCursorClosedError());
237 std::move(callback).Run(blink::mojom::IDBCursorResult::NewErrorResult(
238 blink::mojom::IDBError::New(error.code(), error.message())));
239 return;
240 }
241
242 blink::mojom::IDBCursor::PrefetchCallback aborting_callback =
243 CreateCallbackAbortOnDestruct<blink::mojom::IDBCursor::PrefetchCallback,
244 blink::mojom::IDBCursorResultPtr>(
245 std::move(callback), transaction_);
246
247 transaction_->ScheduleTask(
248 task_type_, BindWeakOperation<IndexedDBCursor>(
249 &IndexedDBCursor::CursorPrefetchIterationOperation,
250 ptr_factory_.GetWeakPtr(), std::move(dispatcher_host),
251 number_to_fetch, std::move(aborting_callback)));
252 }
253
CursorPrefetchIterationOperation(base::WeakPtr<IndexedDBDispatcherHost> dispatcher_host,int number_to_fetch,blink::mojom::IDBCursor::PrefetchCallback callback,IndexedDBTransaction *)254 leveldb::Status IndexedDBCursor::CursorPrefetchIterationOperation(
255 base::WeakPtr<IndexedDBDispatcherHost> dispatcher_host,
256 int number_to_fetch,
257 blink::mojom::IDBCursor::PrefetchCallback callback,
258 IndexedDBTransaction* /*transaction*/) {
259 IDB_TRACE("IndexedDBCursor::CursorPrefetchIterationOperation");
260 leveldb::Status s = leveldb::Status::OK();
261 if (!dispatcher_host)
262 return s;
263
264 std::vector<IndexedDBKey> found_keys;
265 std::vector<IndexedDBKey> found_primary_keys;
266 std::vector<IndexedDBValue> found_values;
267
268 saved_cursor_.reset();
269 // TODO(cmumford): Use IPC::Channel::kMaximumMessageSize
270 const size_t max_size_estimate = 10 * 1024 * 1024;
271 size_t size_estimate = 0;
272
273 // TODO(cmumford): Handle this error (crbug.com/363397). Although this will
274 // properly fail, caller will not know why, and any corruption
275 // will be ignored.
276 for (int i = 0; i < number_to_fetch; ++i) {
277 if (!cursor_ || !cursor_->Continue(&s)) {
278 cursor_.reset();
279 if (s.ok()) {
280 // We've reached the end, so just return what we have.
281 break;
282 }
283 // |transaction_| must be valid for CreateError(), so we can't call
284 // Close() until after calling CreateError().
285 IndexedDBDatabaseError error =
286 CreateError(blink::mojom::IDBException::kUnknownError,
287 "Error continuing cursor.", transaction_);
288 Close();
289 std::move(callback).Run(blink::mojom::IDBCursorResult::NewErrorResult(
290 blink::mojom::IDBError::New(error.code(), error.message())));
291 return s;
292 }
293
294 if (i == 0) {
295 // First prefetched result is always used, so that's the position
296 // a cursor should be reset to if the prefetch is invalidated.
297 saved_cursor_ = cursor_->Clone();
298 }
299
300 found_keys.push_back(cursor_->key());
301 found_primary_keys.push_back(cursor_->primary_key());
302
303 switch (cursor_type_) {
304 case indexed_db::CURSOR_KEY_ONLY:
305 found_values.push_back(IndexedDBValue());
306 break;
307 case indexed_db::CURSOR_KEY_AND_VALUE: {
308 IndexedDBValue value;
309 value.swap(*cursor_->value());
310 size_estimate += value.SizeEstimate();
311 found_values.push_back(value);
312 break;
313 }
314 default:
315 NOTREACHED();
316 }
317 size_estimate += cursor_->key().size_estimate();
318 size_estimate += cursor_->primary_key().size_estimate();
319
320 if (size_estimate > max_size_estimate)
321 break;
322 }
323
324 if (found_keys.empty()) {
325 std::move(callback).Run(blink::mojom::IDBCursorResult::NewEmpty(true));
326 return s;
327 }
328
329 DCHECK_EQ(found_keys.size(), found_primary_keys.size());
330 DCHECK_EQ(found_keys.size(), found_values.size());
331
332 std::vector<blink::mojom::IDBValuePtr> mojo_values;
333 mojo_values.reserve(found_values.size());
334 for (size_t i = 0; i < found_values.size(); ++i) {
335 mojo_values.push_back(
336 IndexedDBValue::ConvertAndEraseValue(&found_values[i]));
337 dispatcher_host->CreateAllExternalObjects(
338 origin_, found_values[i].external_objects,
339 &mojo_values[i]->external_objects);
340 }
341
342 std::move(callback).Run(blink::mojom::IDBCursorResult::NewValues(
343 blink::mojom::IDBCursorValue::New(std::move(found_keys),
344 std::move(found_primary_keys),
345 std::move(mojo_values))));
346 return s;
347 }
348
PrefetchReset(int used_prefetches,int)349 leveldb::Status IndexedDBCursor::PrefetchReset(int used_prefetches,
350 int /* unused_prefetches */) {
351 IDB_TRACE("IndexedDBCursor::PrefetchReset");
352 cursor_.swap(saved_cursor_);
353 saved_cursor_.reset();
354 leveldb::Status s;
355
356 if (closed_)
357 return s;
358 // First prefetched result is always used.
359 if (cursor_) {
360 DCHECK_GT(used_prefetches, 0);
361 for (int i = 0; i < used_prefetches - 1; ++i) {
362 bool ok = cursor_->Continue(&s);
363 DCHECK(ok);
364 }
365 }
366
367 return s;
368 }
369
OnRemoveBinding(base::OnceClosure remove_binding_cb)370 void IndexedDBCursor::OnRemoveBinding(base::OnceClosure remove_binding_cb) {
371 remove_binding_cb_ = std::move(remove_binding_cb);
372 }
373
RemoveBinding()374 void IndexedDBCursor::RemoveBinding() {
375 std::move(remove_binding_cb_).Run();
376 }
377
Close()378 void IndexedDBCursor::Close() {
379 if (closed_)
380 return;
381 IDB_ASYNC_TRACE_END("IndexedDBCursor::open", this);
382 IDB_TRACE("IndexedDBCursor::Close");
383 closed_ = true;
384 cursor_.reset();
385 saved_cursor_.reset();
386 if (transaction_)
387 transaction_->UnregisterOpenCursor(this);
388 transaction_.reset();
389 }
390
391 } // namespace content
392