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 <vector>
8
9 #include "base/bind.h"
10 #include "base/logging.h"
11 #include "content/browser/indexed_db/indexed_db_callbacks.h"
12 #include "content/browser/indexed_db/indexed_db_database_error.h"
13 #include "content/browser/indexed_db/indexed_db_tracing.h"
14 #include "content/browser/indexed_db/indexed_db_transaction.h"
15 #include "content/browser/indexed_db/indexed_db_value.h"
16
17 namespace content {
18
IndexedDBCursor(scoped_ptr<IndexedDBBackingStore::Cursor> cursor,indexed_db::CursorType cursor_type,IndexedDBDatabase::TaskType task_type,IndexedDBTransaction * transaction)19 IndexedDBCursor::IndexedDBCursor(
20 scoped_ptr<IndexedDBBackingStore::Cursor> cursor,
21 indexed_db::CursorType cursor_type,
22 IndexedDBDatabase::TaskType task_type,
23 IndexedDBTransaction* transaction)
24 : task_type_(task_type),
25 cursor_type_(cursor_type),
26 transaction_(transaction),
27 cursor_(cursor.Pass()),
28 closed_(false) {
29 transaction_->RegisterOpenCursor(this);
30 }
31
~IndexedDBCursor()32 IndexedDBCursor::~IndexedDBCursor() {
33 transaction_->UnregisterOpenCursor(this);
34 }
35
Continue(scoped_ptr<IndexedDBKey> key,scoped_ptr<IndexedDBKey> primary_key,scoped_refptr<IndexedDBCallbacks> callbacks)36 void IndexedDBCursor::Continue(scoped_ptr<IndexedDBKey> key,
37 scoped_ptr<IndexedDBKey> primary_key,
38 scoped_refptr<IndexedDBCallbacks> callbacks) {
39 IDB_TRACE("IndexedDBCursor::Continue");
40
41 transaction_->ScheduleTask(
42 task_type_,
43 base::Bind(&IndexedDBCursor::CursorIterationOperation,
44 this,
45 base::Passed(&key),
46 base::Passed(&primary_key),
47 callbacks));
48 }
49
Advance(uint32 count,scoped_refptr<IndexedDBCallbacks> callbacks)50 void IndexedDBCursor::Advance(uint32 count,
51 scoped_refptr<IndexedDBCallbacks> callbacks) {
52 IDB_TRACE("IndexedDBCursor::Advance");
53
54 transaction_->ScheduleTask(
55 task_type_,
56 base::Bind(
57 &IndexedDBCursor::CursorAdvanceOperation, this, count, callbacks));
58 }
59
CursorAdvanceOperation(uint32 count,scoped_refptr<IndexedDBCallbacks> callbacks,IndexedDBTransaction *)60 void IndexedDBCursor::CursorAdvanceOperation(
61 uint32 count,
62 scoped_refptr<IndexedDBCallbacks> callbacks,
63 IndexedDBTransaction* /*transaction*/) {
64 IDB_TRACE("IndexedDBCursor::CursorAdvanceOperation");
65 leveldb::Status s;
66 // TODO(cmumford): Handle this error (crbug.com/363397). Although this will
67 // properly fail, caller will not know why, and any corruption
68 // will be ignored.
69 if (!cursor_ || !cursor_->Advance(count, &s)) {
70 cursor_.reset();
71 callbacks->OnSuccess(static_cast<IndexedDBValue*>(NULL));
72 return;
73 }
74
75 callbacks->OnSuccess(key(), primary_key(), Value());
76 }
77
CursorIterationOperation(scoped_ptr<IndexedDBKey> key,scoped_ptr<IndexedDBKey> primary_key,scoped_refptr<IndexedDBCallbacks> callbacks,IndexedDBTransaction *)78 void IndexedDBCursor::CursorIterationOperation(
79 scoped_ptr<IndexedDBKey> key,
80 scoped_ptr<IndexedDBKey> primary_key,
81 scoped_refptr<IndexedDBCallbacks> callbacks,
82 IndexedDBTransaction* /*transaction*/) {
83 IDB_TRACE("IndexedDBCursor::CursorIterationOperation");
84 leveldb::Status s;
85 // TODO(cmumford): Handle this error (crbug.com/363397). Although this will
86 // properly fail, caller will not know why, and any corruption
87 // will be ignored.
88 if (!cursor_ || !cursor_->Continue(key.get(),
89 primary_key.get(),
90 IndexedDBBackingStore::Cursor::SEEK,
91 &s) || !s.ok()) {
92 cursor_.reset();
93 callbacks->OnSuccess(static_cast<IndexedDBValue*>(NULL));
94 return;
95 }
96
97 callbacks->OnSuccess(this->key(), this->primary_key(), Value());
98 }
99
PrefetchContinue(int number_to_fetch,scoped_refptr<IndexedDBCallbacks> callbacks)100 void IndexedDBCursor::PrefetchContinue(
101 int number_to_fetch,
102 scoped_refptr<IndexedDBCallbacks> callbacks) {
103 IDB_TRACE("IndexedDBCursor::PrefetchContinue");
104
105 transaction_->ScheduleTask(
106 task_type_,
107 base::Bind(&IndexedDBCursor::CursorPrefetchIterationOperation,
108 this,
109 number_to_fetch,
110 callbacks));
111 }
112
CursorPrefetchIterationOperation(int number_to_fetch,scoped_refptr<IndexedDBCallbacks> callbacks,IndexedDBTransaction *)113 void IndexedDBCursor::CursorPrefetchIterationOperation(
114 int number_to_fetch,
115 scoped_refptr<IndexedDBCallbacks> callbacks,
116 IndexedDBTransaction* /*transaction*/) {
117 IDB_TRACE("IndexedDBCursor::CursorPrefetchIterationOperation");
118
119 std::vector<IndexedDBKey> found_keys;
120 std::vector<IndexedDBKey> found_primary_keys;
121 std::vector<IndexedDBValue> found_values;
122
123 saved_cursor_.reset();
124 const size_t max_size_estimate = 10 * 1024 * 1024;
125 size_t size_estimate = 0;
126 leveldb::Status s;
127
128 // TODO(cmumford): Handle this error (crbug.com/363397). Although this will
129 // properly fail, caller will not know why, and any corruption
130 // will be ignored.
131 for (int i = 0; i < number_to_fetch; ++i) {
132 if (!cursor_ || !cursor_->Continue(&s)) {
133 cursor_.reset();
134 break;
135 }
136
137 if (i == 0) {
138 // First prefetched result is always used, so that's the position
139 // a cursor should be reset to if the prefetch is invalidated.
140 saved_cursor_.reset(cursor_->Clone());
141 }
142
143 found_keys.push_back(cursor_->key());
144 found_primary_keys.push_back(cursor_->primary_key());
145
146 switch (cursor_type_) {
147 case indexed_db::CURSOR_KEY_ONLY:
148 found_values.push_back(IndexedDBValue());
149 break;
150 case indexed_db::CURSOR_KEY_AND_VALUE: {
151 IndexedDBValue value;
152 value.swap(*cursor_->value());
153 size_estimate += value.SizeEstimate();
154 found_values.push_back(value);
155 break;
156 }
157 default:
158 NOTREACHED();
159 }
160 size_estimate += cursor_->key().size_estimate();
161 size_estimate += cursor_->primary_key().size_estimate();
162
163 if (size_estimate > max_size_estimate)
164 break;
165 }
166
167 if (!found_keys.size()) {
168 callbacks->OnSuccess(static_cast<IndexedDBValue*>(NULL));
169 return;
170 }
171
172 callbacks->OnSuccessWithPrefetch(
173 found_keys, found_primary_keys, found_values);
174 }
175
PrefetchReset(int used_prefetches,int)176 leveldb::Status IndexedDBCursor::PrefetchReset(int used_prefetches,
177 int /* unused_prefetches */) {
178 IDB_TRACE("IndexedDBCursor::PrefetchReset");
179 cursor_.swap(saved_cursor_);
180 saved_cursor_.reset();
181 leveldb::Status s;
182
183 if (closed_)
184 return s;
185 if (cursor_) {
186 // First prefetched result is always used.
187 DCHECK_GT(used_prefetches, 0);
188 for (int i = 0; i < used_prefetches - 1; ++i) {
189 bool ok = cursor_->Continue(&s);
190 DCHECK(ok);
191 }
192 }
193
194 return s;
195 }
196
Close()197 void IndexedDBCursor::Close() {
198 IDB_TRACE("IndexedDBCursor::Close");
199 closed_ = true;
200 cursor_.reset();
201 saved_cursor_.reset();
202 }
203
204 } // namespace content
205