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